加固生产运维定时器绑定(07-26-internal-ops-recovery-baseline)
这个提交包含在:
@@ -5,6 +5,10 @@ REMOTE_DIR="${REMOTE_DIR:-/opt/hyb-oa-trialrun}"
|
||||
REMOTE_ENV_FILE="${REMOTE_ENV_FILE:-${REMOTE_DIR}/deploy/trialrun/.env}"
|
||||
COMPOSE_FILE="${COMPOSE_FILE:-deploy/trialrun/docker-compose.yml}"
|
||||
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-hyb-oa-trialrun}"
|
||||
POSTGRES_DATA_DIR="${POSTGRES_DATA_DIR:-}"
|
||||
STORAGE_HOST_DIR="${STORAGE_HOST_DIR:-}"
|
||||
BUILD_SHA="${BUILD_SHA:-}"
|
||||
VITE_BUILD_SHA="${VITE_BUILD_SHA:-}"
|
||||
SERVICE_NAME="${SERVICE_NAME:-hyb-oa-warch3-ops}"
|
||||
RUN_AT="${RUN_AT:-07:30}"
|
||||
MODE="${1:-install}"
|
||||
@@ -19,6 +23,20 @@ need_root() {
|
||||
fi
|
||||
}
|
||||
|
||||
validate_data_path() {
|
||||
local name="$1"
|
||||
local value="$2"
|
||||
if [[ -z "$value" || "$value" != /* || ! "$value" =~ ^/[A-Za-z0-9._/-]+$ ]]; then
|
||||
echo "[ERROR] $name 必须显式提供受限字符集的绝对路径" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$value" == "/" || "$value" == */ || "$value" == *"//"* \
|
||||
|| "/$value/" == *"/../"* || "/$value/" == *"/./"* ]]; then
|
||||
echo "[ERROR] $name 不得是根目录、尾随斜杠或包含空、.、.. 路径段" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
uninstall_timer() {
|
||||
need_root
|
||||
systemctl disable --now "${SERVICE_NAME}.timer" >/dev/null 2>&1 || true
|
||||
@@ -42,6 +60,32 @@ install_timer() {
|
||||
echo "[ERROR] COMPOSE_PROJECT_NAME 格式无效" >&2
|
||||
exit 1
|
||||
fi
|
||||
validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"
|
||||
validate_data_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"
|
||||
if [[ "$POSTGRES_DATA_DIR" == "$STORAGE_HOST_DIR" \
|
||||
|| "$POSTGRES_DATA_DIR" == "$STORAGE_HOST_DIR"/* \
|
||||
|| "$STORAGE_HOST_DIR" == "$POSTGRES_DATA_DIR"/* ]]; then
|
||||
echo "[ERROR] PostgreSQL 与附件宿主目录必须相互独立" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$POSTGRES_DATA_DIR/PG_VERSION" ]]; then
|
||||
echo "[ERROR] POSTGRES_DATA_DIR 必须是包含 PG_VERSION 的现存 PGDATA" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d "$STORAGE_HOST_DIR" ]]; then
|
||||
echo "[ERROR] STORAGE_HOST_DIR 必须是现存目录" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "$BUILD_SHA" =~ ^[0-9a-fA-F]{40,64}$ ]]; then
|
||||
echo "[ERROR] BUILD_SHA 必须是 40-64 位十六进制完整提交标识" >&2
|
||||
exit 1
|
||||
fi
|
||||
BUILD_SHA="${BUILD_SHA,,}"
|
||||
VITE_BUILD_SHA="${VITE_BUILD_SHA,,}"
|
||||
if [[ ! "$VITE_BUILD_SHA" =~ ^[0-9a-f]{12}$ || "${BUILD_SHA:0:12}" != "$VITE_BUILD_SHA" ]]; then
|
||||
echo "[ERROR] VITE_BUILD_SHA 必须是 BUILD_SHA 的 12 位十六进制前缀" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local compose_exec
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
@@ -66,6 +110,10 @@ Environment=REMOTE_DIR=${REMOTE_DIR}
|
||||
Environment=REMOTE_ENV_FILE=${REMOTE_ENV_FILE}
|
||||
Environment=COMPOSE_FILE=${COMPOSE_FILE}
|
||||
Environment=COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}
|
||||
Environment=POSTGRES_DATA_DIR=${POSTGRES_DATA_DIR}
|
||||
Environment=STORAGE_HOST_DIR=${STORAGE_HOST_DIR}
|
||||
Environment=BUILD_SHA=${BUILD_SHA}
|
||||
Environment=VITE_BUILD_SHA=${VITE_BUILD_SHA}
|
||||
Environment=LIVE_SMOKE_BASE_URL=https://oa.hyinda.com
|
||||
Environment=WARCH3_SEND_ALERT=1
|
||||
Environment=WARCH3_OPS_ALERT_SEND=1
|
||||
@@ -99,7 +147,7 @@ case "$MODE" in
|
||||
uninstall_timer
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Usage: REMOTE_DIR=/opt/hyb-oa-trialrun bash deploy/trialrun/install-warch3-ops-timer.sh [install|uninstall]"
|
||||
echo "Usage: REMOTE_DIR=/opt/hyb-oa-trialrun COMPOSE_PROJECT_NAME=<project> POSTGRES_DATA_DIR=<absolute-pgdata> STORAGE_HOST_DIR=<absolute-storage> BUILD_SHA=<full-sha> VITE_BUILD_SHA=<12-char-prefix> bash deploy/trialrun/install-warch3-ops-timer.sh [install|uninstall]"
|
||||
;;
|
||||
*)
|
||||
echo "[ERROR] unsupported mode: $MODE" >&2
|
||||
|
||||
@@ -241,6 +241,37 @@ for (const [name, content] of [
|
||||
if (!content.includes('-p "$COMPOSE_PROJECT_NAME"')) {
|
||||
errors.push(`${name} 必须向 Compose 显式传入独立 project name`);
|
||||
}
|
||||
if (!content.includes('"$value" == *"//"*')
|
||||
|| !content.includes('"/$value/" == *"/../"*')
|
||||
|| !content.includes('"/$value/" == *"/./"*')) {
|
||||
errors.push(`${name} 缺少绝对数据路径的空段与点路径段门禁`);
|
||||
}
|
||||
if (content.includes('[[:space:]]*)($|/)')) {
|
||||
errors.push(`${name} 不得以可匹配空字符串的路径段正则拒绝全部绝对路径`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const requiredFragment of [
|
||||
'POSTGRES_DATA_DIR="${POSTGRES_DATA_DIR:-}"',
|
||||
'STORAGE_HOST_DIR="${STORAGE_HOST_DIR:-}"',
|
||||
'BUILD_SHA="${BUILD_SHA:-}"',
|
||||
'VITE_BUILD_SHA="${VITE_BUILD_SHA:-}"',
|
||||
'validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"',
|
||||
'validate_data_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"',
|
||||
'"$POSTGRES_DATA_DIR" == "$STORAGE_HOST_DIR"/*',
|
||||
'"$STORAGE_HOST_DIR" == "$POSTGRES_DATA_DIR"/*',
|
||||
'"$POSTGRES_DATA_DIR/PG_VERSION"',
|
||||
'"$BUILD_SHA" =~ ^[0-9a-fA-F]{40,64}$',
|
||||
'"$VITE_BUILD_SHA" =~ ^[0-9a-f]{12}$',
|
||||
'"${BUILD_SHA:0:12}" != "$VITE_BUILD_SHA"',
|
||||
'Environment=POSTGRES_DATA_DIR=${POSTGRES_DATA_DIR}',
|
||||
'Environment=STORAGE_HOST_DIR=${STORAGE_HOST_DIR}',
|
||||
'Environment=BUILD_SHA=${BUILD_SHA}',
|
||||
'Environment=VITE_BUILD_SHA=${VITE_BUILD_SHA}',
|
||||
]) {
|
||||
if (!timerScriptContent.includes(requiredFragment)) {
|
||||
errors.push(`install-warch3-ops-timer.sh 缺少活动部署环境绑定: ${requiredFragment}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const requiredFragment of [
|
||||
|
||||
在新工单中引用
屏蔽一个用户