Files
knowledge-governance/deploy/cloud-test/run-release-executor.sh
姚章浩 a50cd73423
build / build (push) Successful in 1m21s
fix(deploy): 校验云执行器授权配置与稳定启动
2026-10-31 04:26:54 +08:00

94 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# 在云测试主机上启动/更新"发布执行器"容器(由父在目标机执行,不在本机远程包装)。
#
# 为什么单独 docker run 而不是塞进 weknora-test-v071 这个 compose 项目:
# 维护协调器要求项目里每个服务都被声明为写入者或"非写入者",执行器不是目标的一部分,
# 放进同一个项目只会让拓扑清单多一条无关服务。它只需要挂上目标项目的网络即可。
set -euo pipefail
IMAGE="${RELEASE_EXECUTOR_IMAGE:-}"
NETWORK="${TARGET_NETWORK:-weknora-test-v071-network}"
PG_SOCKET_DIR="${RELEASE_PG_SOCKET_DIR:-/run/kgtest-pg}"
PGPASS_FILE="${RELEASE_PGPASS_FILE:-/srv/knowledge-governance/governance.pgpass}"
ENV_FILE="${RELEASE_EXECUTOR_ENV:-/srv/knowledge-governance/release-executor.env}"
ARCHIVE="${RELEASE_ARCHIVE_ROOT:-/srv/knowledge-governance/library}"
BUNDLES="${RELEASE_BUNDLE_DIR:-/srv/knowledge-governance/bundles}"
JOURNAL="${RELEASE_JOURNAL_ROOT:-/srv/knowledge-governance/target-journal}"
GATE_STATE="${RELEASE_GATE_STATE:-/srv/kgtest-gate/state}"
NAME="${RELEASE_EXECUTOR_NAME:-kgtest-release-executor}"
die() { echo "release-executor: $*" >&2; exit 1; }
[ -n "$IMAGE" ] || die "RELEASE_EXECUTOR_IMAGE 未设置(必须是与已验收提交同 sha 的 worker 镜像)"
[ "$(id -u)" -eq 0 ] || die "需要 root:要读 docker.sock 并读写 0700 归档目录"
docker network inspect "$NETWORK" >/dev/null 2>&1 || die "网络 $NETWORK 不存在(先部署目标项目)"
[ -f "$ENV_FILE" ] || die "缺少 env 文件 $ENV_FILE"
[ "$(stat -c '%a' "$ENV_FILE")" = "600" ] || die "$ENV_FILE 必须是 0600(里面有目标凭据)"
grep -q '^LIBRARY_WORKER_ROLE=release-executor$' "$ENV_FILE" || die "$ENV_FILE 里 LIBRARY_WORKER_ROLE 必须是 release-executor"
[ -d "$GATE_STATE" ] || die "闸门状态目录 $GATE_STATE 不存在(先部署 ingress 闸门)"
[ -d "$PG_SOCKET_DIR" ] || die "治理 PG socket 目录不存在"
[ -s "$PGPASS_FILE" ] && [ "$(stat -c '%a' "$PGPASS_FILE")" = "600" ] || die "治理 pgpass 必须是非空 0600 文件"
grep -qx 'PGPASSFILE=/run/secrets/governance_pgpass' "$ENV_FILE" || die "PGPASSFILE 必须指向容器内治理密码文件"
AUTH_TOKEN_FILE="${RELEASE_AUTH_TOKEN_FILE:-/srv/knowledge-governance/production_release_auth_token}"
# Parse only named fields as data, never source a credential-bearing dotenv.
read_setting() {
local wanted="$1" line value="" found=0
while IFS= read -r line || [ -n "$line" ]; do
if [[ "$line" == "$wanted="* ]]; then
((found += 1))
value="${line#*=}"
fi
done < "$ENV_FILE"
[ "$found" -le 1 ] || die "重复配置字段:$wanted"
printf '%s' "$value"
}
auth_mount=()
enabled="$(read_setting PRODUCTION_RELEASE_REQUESTS_ENABLED)"
case "$enabled" in
true|TRUE|True|1|t|T)
author="$(read_setting PRODUCTION_RELEASE_AUTHORIZER_REF)"
[ -n "$author" ] && [ "${#author}" -le 128 ] && [[ "$author" != *[$'\r\n\t']* ]] || die "PRODUCTION_RELEASE_AUTHORIZER_REF 缺失或非法"
token_path="$(read_setting PRODUCTION_RELEASE_AUTH_TOKEN_FILE)"
[ "$token_path" = /run/secrets/production_release_auth_token ] || die "PRODUCTION_RELEASE_AUTH_TOKEN_FILE 必须指向 /run/secrets/production_release_auth_token"
[ -f "$AUTH_TOKEN_FILE" ] && [ ! -L "$AUTH_TOKEN_FILE" ] && [ -s "$AUTH_TOKEN_FILE" ] && [ "$(stat -c '%a' "$AUTH_TOKEN_FILE")" = 600 ] || die "授权 token 必须是非空普通 0600 文件"
auth_mount=(-v "$AUTH_TOKEN_FILE:$token_path:ro")
;;
false|FALSE|False|0|f|F|"") ;;
*) die "PRODUCTION_RELEASE_REQUESTS_ENABLED 不是有效布尔值" ;;
esac
install -d -m 0700 "$ARCHIVE" "$JOURNAL" "$BUNDLES"
install -d -m 0700 "$ARCHIVE/blobs" "$ARCHIVE/versions"
if docker inspect "$NAME" >/dev/null 2>&1; then
echo "release-executor: 已存在,按同一 env 重建(先停,便于看清失败原因)"
docker rm -f "$NAME" >/dev/null
fi
docker run -d --name "$NAME" \
--user 0:0 \
--pull never \
--restart unless-stopped \
--network "$NETWORK" \
--label com.nous.role=release-executor \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$ARCHIVE:/var/lib/knowledge-governance/library" \
-v "$BUNDLES:$BUNDLES" \
-v "$JOURNAL:/var/lib/knowledge-governance/target-journal" \
-v "$GATE_STATE:/gate-state" \
-v "$PG_SOCKET_DIR:/run/kgtest-pg:ro" \
-v "$PGPASS_FILE:/run/secrets/governance_pgpass:ro" \
"${auth_mount[@]}" \
--env-file "$ENV_FILE" \
"$IMAGE" /app/worker >/dev/null
# A created container or docker-ps exit code is not a live executor.
for attempt in 1 2 3; do
sleep 2
state="$(docker inspect --format '{{.State.Status}} {{.RestartCount}}' "$NAME")" || die "无法读取执行器状态"
[ "$state" = 'running 0' ] || die "执行器未稳定运行,请检查目标机容器日志"
done
echo "release-executor: 启动检查通过(连续 running、无重启);业务验收仍须核对作业执行结果"