Report the real identity marker in svc adopt and the deploy gate
svc.sh adopt always printed db_marker=established, even when the installed artifact lacked identity capability and the marker stayed pending. It now reads the marker back and warns when identity was not established. The deploy identity gate reported a pre-v27 database (identity table missing) as a foreign row or mismatch. It now explains that the schema predates the identity table and gives the backup, stop, migrate and redeploy steps. It still refuses before stopping the old service and never migrates on its own. Why: both messages misled the 2026-10-21 GenUI prod upgrade. Evidence: agent-exec unit dev-svc-identity-adopt-gate-messages (cursor, attempt 1, terminal=success); python3 -m unittest tests.test_svc_identity: 31 tests OK (1 skipped). Impact: svc.sh operator messages for adopt and the deploy identity gate; no change to success paths. Refs: Dev task fix/genui-prod-upgrade-followups. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
8c8cda03cf
commit
6023900eb6
+32
-3
@@ -1355,6 +1355,8 @@ svc_identity_run() {
|
||||
svc_db_identity_ensure() {
|
||||
# 仅 fresh/pending 或显式 adopt 调用。established 缺行不得走这里自动补。
|
||||
# 非 agent 或无能力:跳过。可选参数 BIN:部署候选。
|
||||
# SVC_IDENTITY_LAST_OUT:最近一次 identity CLI 输出,供候选门禁区分失败原因。
|
||||
SVC_IDENTITY_LAST_OUT=""
|
||||
local bin="${1:-${ENV_BIN:-}}"
|
||||
manifest_ok || return 0
|
||||
local kind; kind="$(svc_get kind)"
|
||||
@@ -1365,6 +1367,7 @@ svc_db_identity_ensure() {
|
||||
IFS=$'\t' read -r iid dbref data <<<"$line"
|
||||
local out rc=0
|
||||
out="$(svc_identity_run "$bin" ensure "$iid" "$dbref" "$data" 2>&1)" || rc=$?
|
||||
SVC_IDENTITY_LAST_OUT="$out"
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
c_err "[$ENV_NAME] DB 身份 ensure 失败:$out"
|
||||
return 1
|
||||
@@ -1374,6 +1377,7 @@ svc_db_identity_ensure() {
|
||||
}
|
||||
|
||||
svc_db_identity_verify() {
|
||||
SVC_IDENTITY_LAST_OUT=""
|
||||
manifest_ok || return 0
|
||||
local kind; kind="$(svc_get kind)"
|
||||
[ "$kind" = agent ] || return 0
|
||||
@@ -1384,6 +1388,7 @@ svc_db_identity_verify() {
|
||||
IFS=$'\t' read -r iid dbref data <<<"$line"
|
||||
local out rc=0
|
||||
out="$(svc_identity_run "$bin" verify "$iid" "$dbref" "$data" 2>&1)" || rc=$?
|
||||
SVC_IDENTITY_LAST_OUT="$out"
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
c_err "[$ENV_NAME] DB 身份校验失败,拒绝启动:$out"
|
||||
c_err " 先调查库文件与身份行;确认归属后 scripts/svc.sh $ENV_NAME adopt --confirm $iid"
|
||||
@@ -1395,6 +1400,22 @@ svc_db_identity_verify() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# svc_identity_schema_missing SRCBIN:最近一次 identity CLI 报身份表缺失(库 schema
|
||||
# 早于 v27)时打印迁移指引并返回 0;否则返回 1,调用方沿用原失败提示。不自动迁移。
|
||||
svc_identity_schema_missing() {
|
||||
case "${SVC_IDENTITY_LAST_OUT:-}" in
|
||||
*"identity table missing"*) ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
local data_root
|
||||
data_root="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["identity"]["data_root"])' "$ENV_DIR/manifest.json" 2>/dev/null)" \
|
||||
|| data_root="<data_root>"
|
||||
c_err "[$ENV_NAME] 数据库 schema 早于身份表(pre-v27,identity table missing):候选无法补齐/验证 DB 身份;旧服务/旧制品/登记保持不变"
|
||||
c_err " 处理:先备份数据库 → scripts/svc.sh $ENV_NAME stop → 用候选制品执行 $1 migrate --data $data_root(PostgreSQL:DSN 只经环境变量 AGENT_SYSTEM_DSN 传入,不进 argv)→ 再次部署"
|
||||
c_err " svc 不会自动迁移;本次暂存的候选在拒绝后清理,迁移请使用同一版本的制品"
|
||||
return 0
|
||||
}
|
||||
|
||||
# svc_deploy_identity_gate SRCBIN:部署候选的身份门禁——在停旧服务、覆盖制品之前
|
||||
# 执行(2026-10-12 rdm1/rdmcand 事故:停服→覆盖→启动即被身份门禁拒绝,只剩
|
||||
# 「登记版本≠磁盘制品」的混合状态且无恢复路径)。规则:
|
||||
@@ -1425,7 +1446,8 @@ svc_deploy_identity_gate() {
|
||||
data_root="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["identity"]["data_root"])' "$ENV_DIR/manifest.json")"
|
||||
if [ -f "$data_root/agent-system.db" ] || { [ "${DB_MODE:-}" = pg ] && [ -n "${DB_DSN:-}" ]; }; then
|
||||
svc_db_identity_ensure "$srcbin" || {
|
||||
c_err "[$ENV_NAME] 候选补齐 DB 身份失败(外宾行/不一致 fail-closed);旧服务/旧制品/登记保持不变"
|
||||
svc_identity_schema_missing "$srcbin" \
|
||||
|| c_err "[$ENV_NAME] 候选补齐 DB 身份失败(外宾行/不一致 fail-closed);旧服务/旧制品/登记保持不变"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
@@ -1433,7 +1455,8 @@ svc_deploy_identity_gate() {
|
||||
*)
|
||||
# established:只验证,不补行。缺行或不匹配在停旧服务前拒绝。
|
||||
svc_db_identity_verify "$srcbin" || {
|
||||
c_err "[$ENV_NAME] 候选无法验证已建立的 DB 身份;旧服务/旧制品/登记保持不变"
|
||||
svc_identity_schema_missing "$srcbin" \
|
||||
|| c_err "[$ENV_NAME] 候选无法验证已建立的 DB 身份;旧服务/旧制品/登记保持不变"
|
||||
return 1
|
||||
}
|
||||
;;
|
||||
@@ -6507,7 +6530,13 @@ finally:
|
||||
PY
|
||||
svc_db_marker_set pending || exit 1
|
||||
svc_db_identity_ensure || { c_err "[$ENV_NAME] adopt ensure 失败"; exit 1; }
|
||||
c_ok "[$ENV_NAME] adopt 完成,db_marker=established" ;;
|
||||
local adopt_marker; adopt_marker="$(svc_db_marker)"
|
||||
if [ "$adopt_marker" = established ]; then
|
||||
c_ok "[$ENV_NAME] adopt 完成,db_marker=established"
|
||||
else
|
||||
c_warn "[$ENV_NAME] adopt 未建立 DB 身份:db_marker=$adopt_marker(当前已装制品不具备 identity 能力,无法建立身份)"
|
||||
c_warn " 部署具备 identity ensure/verify 的制品后,由候选身份门禁补齐"
|
||||
fi ;;
|
||||
status) env_status_detail ;;
|
||||
logs) [ -f "$ENV_LOG" ] && tail -n "${CLI_LOGS_N:-50}" -f "$ENV_LOG" || c_warn "[$ENV_NAME] 暂无日志" ;;
|
||||
esac
|
||||
|
||||
@@ -413,6 +413,51 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
hang.chmod(0o755)
|
||||
self.assertIn("rc=1", probe(hang), "探测挂死须有界返回无能力")
|
||||
|
||||
def test_U1_adopt_identityless_artifact_reports_pending(self):
|
||||
"""已装制品无 identity 能力:adopt 的 ensure 静默跳过,marker 留在 pending——
|
||||
输出必须如实报 pending,不得宣称 db_marker=established。"""
|
||||
self.seed_agent("adx", 9454)
|
||||
self.start_ok("adx")
|
||||
iid = self.manifest("adx")["instance_id"]
|
||||
self.svc("adx", "stop")
|
||||
self.install_noid_bin("adx")
|
||||
result = self.svc("adx", "adopt", "--confirm", iid)
|
||||
self.assertNotIn("db_marker=established", result.stdout)
|
||||
self.assertIn("db_marker=pending", result.stdout)
|
||||
self.assertEqual(self.manifest("adx")["identity"]["db_marker"], "pending")
|
||||
|
||||
def test_U1_update_pre_v27_schema_refused_with_migrate_guidance(self):
|
||||
"""候选有 identity 能力但库 schema 早于身份表:停服前拒绝,给出
|
||||
备份→stop→migrate→再部署的指引,而非「外宾行」误导;不自动迁移。"""
|
||||
cand = self.root / "stub-agent-system-noschema"
|
||||
cand.write_text(
|
||||
'#!/usr/bin/env bash\ncase "${1:-}" in identity) '
|
||||
'echo "identity ${2:-ensure}: identity table missing" >&2; exit 1 ;; '
|
||||
'*) exec "%s" "$@" ;; esac\n' % self.stub)
|
||||
cand.chmod(0o755)
|
||||
remote, old_sha, _ = self._make_track_repo(cand, "agent-system")
|
||||
self.git_ok(self.root, "clone", str(remote), str(self.root / "srccache"))
|
||||
self.seed_agent("scx", 9455)
|
||||
self.start_ok("scx")
|
||||
self.set_kv("scx", "source", "dev@%s" % old_sha)
|
||||
mpath = self.runtime("scx") / "manifest.json"
|
||||
m = json.loads(mpath.read_text(encoding="utf-8"))
|
||||
m["identity"]["db_marker"] = "pending"
|
||||
mpath.write_text(json.dumps(m, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
pid = self.pid_of("scx")
|
||||
bin_before = (self.runtime("scx") / "bin" / "agent-system").read_bytes()
|
||||
|
||||
result = self.svc("update", "scx", check=False)
|
||||
self.assertNotEqual(result.returncode, 0, result.stdout)
|
||||
self.assertIn("migrate --data", result.stdout)
|
||||
self.assertIn("AGENT_SYSTEM_DSN", result.stdout)
|
||||
self.assertNotIn("外宾行", result.stdout)
|
||||
self.assertTrue(self.running("scx"), "拒绝必须发生在停旧服务之前")
|
||||
self.assertEqual(self.pid_of("scx"), pid)
|
||||
self.assertEqual((self.runtime("scx") / "bin" / "agent-system").read_bytes(),
|
||||
bin_before, "旧制品不得被覆盖")
|
||||
self.svc("scx", "stop")
|
||||
|
||||
def test_I3_start_ensures_sqlite_identity(self):
|
||||
self.seed_agent("idb1", 9441)
|
||||
self.start_ok("idb1")
|
||||
|
||||
Reference in New Issue
Block a user