Files
dsh_ai1net_server/交付物/插件建库权限-一次性执行.sh

104 lines
5.4 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ==============================================================================
# 🔴 2026-09-23 已作废,⛔ 不得执行。
# 原因:PG 显式禁止从函数内执行 CREATE DATABASE(SECURITY DEFINER 路径无效)。
# 替代件:交付物/建库权限-D档被证伪与修正判定-20260923.md
# ==============================================================================
# D 档一次性落地:建库函数 + 7 条验收 + 锁管理(幂等 · 可重复跑)
# 用法: bash "E:/ProgramData/AIProject/aliyun-dsh-server/交付物/插件建库权限-一次性执行.sh"
# 设计目标:**把占锁窗口压到 ~40 秒**(抢不到就立刻退出,不空转)
# 退出码: 0=全绿 2=DDL/验收失败 3=抢锁失败(别的会话在跑)
# ⚠️ 本脚本会 `su postgres` 建 SECURITY DEFINER 函数 —— 已由用户 2026-09-22 19:3x 拍板 D
# ==============================================================================
set -u
GUARD="D:/github/dsh_shenxian/dsh-server-docs/scripts/handoff-guard.sh"
HOST="[email protected]"
ME="插件投放与分库线-执行棒(抢间隙)"
# ── 1 抢全局执行锁(抢不到立刻走人)─────────────────────────────────────────
CLAIM_OUT="$(bash "$GUARD" --claim-exec "$ME" 2>&1)"
if printf '%s' "$CLAIM_OUT" | grep -q "✗"; then
echo "LOCK_BUSY :: $(printf '%s' "$CLAIM_OUT" | grep -m1 '占用者' | cut -c1-80)"
echo "→ 停手(R9:不得删锁 / 不得接管)。过一会儿再跑本脚本即可。"
exit 3
fi
echo "LOCK_HELD=yes"
# 无论成败都放锁(这是抢间隙策略的前提)
trap 'bash "$GUARD" --release-exec >/dev/null 2>&1; echo "LOCK_RELEASED=yes"' EXIT
# ── 2 落地 DDL(幂等)────────────────────────────────────────────────────────
echo "--- 2 落地 DDL ---"
ssh -o ConnectTimeout=8 -o BatchMode=yes "$HOST" 'bash -s' <<'REMOTE'
set -u
cat > /tmp/.pdl-d.sql <<'SQL'
CREATE SCHEMA IF NOT EXISTS dshs_int AUTHORIZATION postgres;
REVOKE ALL ON SCHEMA dshs_int FROM PUBLIC;
GRANT USAGE ON SCHEMA dshs_int TO dshs;
CREATE OR REPLACE FUNCTION dshs_int.create_plugin_database(p_name text)
RETURNS text
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = pg_catalog
AS $fn$
DECLARE
v_owner constant text := 'dshs';
BEGIN
IF p_name IS NULL OR p_name !~ '^dshs_pl_[a-z][a-z0-9_]{0,40}$' THEN
RAISE EXCEPTION 'invalid plugin database name: %', p_name USING ERRCODE = '22023';
END IF;
IF EXISTS (SELECT 1 FROM pg_database WHERE datname = p_name) THEN
RETURN 'exists';
END IF;
EXECUTE format('CREATE DATABASE %I OWNER %I ENCODING %L TEMPLATE template0',
p_name, v_owner, 'UTF8');
RAISE LOG 'dshs plugin db created: %', p_name;
RETURN 'created';
EXCEPTION
WHEN duplicate_database THEN
RETURN 'exists';
END;
$fn$;
REVOKE ALL ON FUNCTION dshs_int.create_plugin_database(text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION dshs_int.create_plugin_database(text) TO dshs;
SQL
su postgres -c 'cd /tmp && psql -h /var/run/postgresql -p 15432 -d dshs -X -v ON_ERROR_STOP=1 -q -f /tmp/.pdl-d.sql' 2>&1 | head -10
echo "DDL_RC=$?"
rm -f /tmp/.pdl-d.sql
REMOTE
[ $? -ne 0 ] && { echo "DDL_FAILED"; exit 2; }
# ── 3 验收 7 条 ──────────────────────────────────────────────────────────────
echo "--- 3 验收 ---"
ssh -o ConnectTimeout=8 -o BatchMode=yes "$HOST" 'bash -s' <<'REMOTE'
P() { su postgres -c "cd /tmp && psql -h /var/run/postgresql -p 15432 -d dshs -X -A -t -q -c \"$1\"" 2>&1 | head -2; }
U() { python3 - <<'PY'
import glob,re,os
url=""
for f in ["/etc/dshs.env"]+glob.glob("/etc/systemd/system/dshs.service.d/*.conf"):
try:
for line in open(f,encoding="utf-8",errors="replace"):
m=re.search(r'DSHS_DB_URL\s*=\s*"?([^"\s]+)"?',line)
if m: url=m.group(1)
except Exception: pass
import urllib.parse as up
print(up.urlparse(url).password or "")
PY
}
export PGPASSWORD="$(U)"
D() { psql -h 127.0.0.1 -p 15432 -U dshs -d dshs -X -A -t -q -c "$1" 2>&1 | head -2; }
echo "① create = $(D "select dshs_int.create_plugin_database('dshs_pl_selftest')")"
echo "② 幂等 = $(D "select dshs_int.create_plugin_database('dshs_pl_selftest')")"
echo "③ 库与属主 = $(P "select datname||'/'||pg_get_userbyid(datdba) from pg_database where datname='dshs_pl_selftest'")"
echo "④ 白名单(evil) = $(D "select dshs_int.create_plugin_database('evil')" | head -1)"
echo "④' 注入串 = $(D "select dshs_int.create_plugin_database('dshs_pl_x; DROP DATABASE dshs')" | head -1)"
echo "⑤ dshs 权限(须 f|f) = $(P "select rolsuper||'|'||rolcreatedb from pg_roles where rolname='dshs'")"
echo "⑥ 无旁路 = $(D 'create database pdl_should_fail' | head -1)"
echo "⑦ 可连新库+建schema = $(PGPASSWORD="$PGPASSWORD" psql -h 127.0.0.1 -p 15432 -U dshs -d dshs_pl_selftest -X -A -t -q -c 'create schema s_test' 2>&1 | head -1)"
echo "→ 清理 = $(P 'DROP DATABASE dshs_pl_selftest' >/dev/null 2>&1 && echo dropped)"
echo "→ 残留库数(须0) = $(P "select count(*) from pg_database where datname like 'dshs_pl_%'")"
REMOTE
echo "DONE"