#!/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、无重启);业务验收仍须核对作业执行结果"