Bind svc user CLI to instance storage and reject lock bypass
Why: U1 requires account ops to hit manifest data_root/DSN with OpenExisting and refuse missing flock or default ./data. Evidence: python3 -m unittest tests.test_svc_user_binding (T1–T5) + test_svc_identity Impact: svc <env> user … calls ENV_BIN with --open-existing; deploy/user fail closed without flock. Refs: docs/svc-deploy/u1-instance-account-binding.md; Backend OpenExisting (PR #171) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+74
-6
@@ -871,18 +871,25 @@ port_owner() {
|
||||
|
||||
# ---- 生命周期互斥锁与期望运行态(autostart;见 help recovery)----
|
||||
# svc_lock MODE:对当前服务取 .start.lock 互斥锁。MODE=wait 阻塞(最长 300s),
|
||||
# MODE=-n 非阻塞(抢不到返回 1)。flock 缺失时降级为无锁直通(尽力而为)。
|
||||
# MODE=-n 非阻塞(抢不到返回 1)。flock 缺失或等待超时 → 拒绝(不得直通冲突操作)。
|
||||
# 锁随进程退出自动释放(fd 关闭);长驻循环(recover)用 svc_unlock 逐服务显式释放。
|
||||
SVC_LOCK_HELD=0
|
||||
svc_lock() {
|
||||
[ "$SVC_LOCK_HELD" = 1 ] && return 0
|
||||
command -v flock >/dev/null 2>&1 || { SVC_LOCK_HELD=1; return 0; }
|
||||
if ! command -v flock >/dev/null 2>&1; then
|
||||
c_err "[$ENV_NAME] 缺少 flock:拒绝生命周期/账户冲突操作(安装 util-linux 后重试)"
|
||||
return 1
|
||||
fi
|
||||
mkdir -p "$ENV_DIR" 2>/dev/null || return 1
|
||||
exec 9>>"$ENV_DIR/.start.lock" || return 1
|
||||
if [ "$1" = "-n" ]; then
|
||||
flock -n 9 || { exec 9>&-; return 1; }
|
||||
else
|
||||
flock -w 300 9 || c_warn "[$ENV_NAME] 生命周期锁等待超时(继续执行,注意并发)"
|
||||
if ! flock -w 300 9; then
|
||||
exec 9>&-
|
||||
c_err "[$ENV_NAME] 生命周期锁等待超时:拒绝继续(避免并发冲突)"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
SVC_LOCK_HELD=1
|
||||
return 0
|
||||
@@ -1384,6 +1391,66 @@ materialize_release() {
|
||||
install_artifacts "$bin" "$art/plugins" || return 1
|
||||
}
|
||||
|
||||
# ---- agent:账户 CLI(绑定实例存储;禁止回落默认 ./data)----
|
||||
# 用法:scripts/svc.sh <env> user <create|list|...> [--owner …]
|
||||
# 从实例最终配置 / manifest identity 解析 data/DSN,强制 --open-existing,
|
||||
# 调用 ENV_BIN 对应版本。拒绝调用方覆盖 --data/--dsn/--open-existing。
|
||||
agent_do_user() {
|
||||
local a
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--data|--dsn|--open-existing|--data=*|--dsn=*|--open-existing=*)
|
||||
c_err "[$ENV_NAME] 账户操作禁止覆盖存储绑定(勿传 --data/--dsn/--open-existing;由实例 manifest 决定)"
|
||||
return 2 ;;
|
||||
esac
|
||||
done
|
||||
svc_lock wait || { c_err "[$ENV_NAME] 无法获取生命周期锁(账户操作与启停互斥)"; return 1; }
|
||||
# 有 manifest 时以 identity.data_root 为权威,拒绝 ENV_DATA 漂移到默认 ./data。
|
||||
if [ -f "$ENV_DIR/manifest.json" ] && command -v python3 >/dev/null 2>&1; then
|
||||
local mf_root
|
||||
mf_root="$(python3 -c 'import json,sys; m=json.load(open(sys.argv[1])); print(m["identity"]["data_root"])' \
|
||||
"$ENV_DIR/manifest.json" 2>/dev/null || true)"
|
||||
if [ -n "$mf_root" ]; then
|
||||
ENV_DATA="$mf_root"
|
||||
fi
|
||||
fi
|
||||
case "${ENV_DATA:-}" in
|
||||
""|./data|data)
|
||||
c_err "[$ENV_NAME] 未绑定数据目录:拒绝账户操作(不回落默认 ./data)"
|
||||
return 1 ;;
|
||||
esac
|
||||
resolve_db_config || {
|
||||
c_err "[$ENV_NAME] 数据库绑定无效:拒绝账户操作(不回落默认 ./data)"
|
||||
return 1
|
||||
}
|
||||
if [ "$DB_MODE" = mock ]; then
|
||||
c_err "[$ENV_NAME] mock 档不支持账户操作"
|
||||
return 1
|
||||
fi
|
||||
if [ ! -x "${ENV_BIN:-}" ]; then
|
||||
c_err "[$ENV_NAME] 实例二进制不存在:$ENV_BIN(先 deploy)"
|
||||
return 1
|
||||
fi
|
||||
if [ "$DB_MODE" = pg ] && [ -z "${DB_DSN:-}" ] && [ "${DB_SOURCE:-}" != conffile ]; then
|
||||
c_err "[$ENV_NAME] PostgreSQL 档缺少 DSN:拒绝账户操作"
|
||||
return 1
|
||||
fi
|
||||
# 嵌入式档:数据文件必须已存在(OpenExisting);PG 档由 --open-existing 在后端核验。
|
||||
if [ "$DB_MODE" != pg ]; then
|
||||
if [ ! -d "$ENV_DATA" ] || [ ! -f "$ENV_DATA/agent-system.db" ]; then
|
||||
c_err "[$ENV_NAME] 既有库不存在:$ENV_DATA/agent-system.db(OpenExisting:拒绝创建空库)"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
local bin_args=(--open-existing --data "$ENV_DATA")
|
||||
if [ "$DB_MODE" = pg ] && [ -n "${DB_DSN:-}" ]; then
|
||||
bin_args+=(--dsn "$DB_DSN")
|
||||
fi
|
||||
c_info "[$ENV_NAME] 账户 CLI → $ENV_BIN user $* (data=$ENV_DATA open-existing)"
|
||||
# 子命令参数在前,绑定 flags 在后(Go flag 不区分顺序)。
|
||||
AGENT_SYSTEM_DSN="${DB_DSN:-}" "$ENV_BIN" user "$@" "${bin_args[@]}"
|
||||
}
|
||||
|
||||
# ---- agent:部署与启动 ----
|
||||
|
||||
agent_do_deploy() {
|
||||
@@ -3954,17 +4021,18 @@ main() {
|
||||
# 动作词可省略(省略 = deploy);flags 可直接跟随服务名。
|
||||
local action="deploy"
|
||||
case "${1:-}" in
|
||||
deploy|stop|start|restart|status|logs|db|config) action="$1"; shift ;;
|
||||
deploy|stop|start|restart|status|logs|db|config|init|user) action="$1"; shift ;;
|
||||
esac
|
||||
if [ "$action" = logs ] && [ "${1:-}" != "" ] && [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
CLI_LOGS_N="$1"; shift
|
||||
fi
|
||||
# db / config 子命令:自带参数解析(位置参数不走 parse_args;未知 flags 由其自行拒绝)。
|
||||
if [ "$action" = db ] || [ "$action" = config ]; then
|
||||
# db / config / user 子命令:自带参数解析(位置参数不走 parse_args)。
|
||||
if [ "$action" = db ] || [ "$action" = config ] || [ "$action" = user ]; then
|
||||
env_config "$ENV_NAME"
|
||||
case "$action" in
|
||||
db) svc_db_bind "$@" || exit $? ;;
|
||||
config) svc_config "$@" || exit $? ;;
|
||||
user) agent_do_user "$@" || exit $? ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user