159 lines
4.8 KiB
Bash
159 lines
4.8 KiB
Bash
#!/usr/bin/env bash
|
||
# 将仓库 idwms/*.json 渲染密钥后写入 Consul KV
|
||
# 用法:
|
||
# ./scripts/sync-to-consul.sh # 同步全部
|
||
# ./scripts/sync-to-consul.sh admin # 只同步 idwms/admin
|
||
# RESTART_APIS=1 ./scripts/sync-to-consul.sh
|
||
#
|
||
# 密钥来源(优先):
|
||
# 1) 环境变量 MYSQL_ROOT_PASSWORD / REDIS_PASSWORD / ...
|
||
# 2) K8s Secret prod-credentials(及可选 jwt-secret)
|
||
#
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||
export KUBECONFIG="${KUBECONFIG:-/etc/rancher/k3s/k3s.yaml}"
|
||
NS="${NAMESPACE:-prod}"
|
||
CONSUL_HTTP_ADDR="${CONSUL_HTTP_ADDR:-}"
|
||
RESTART_APIS="${RESTART_APIS:-0}"
|
||
IDWMS_DIR="${ROOT}/idwms"
|
||
|
||
need() { command -v "$1" >/dev/null || { echo "FAIL: 需要 $1"; exit 1; }; }
|
||
need kubectl
|
||
need curl
|
||
need jq
|
||
command -v envsubst >/dev/null || { echo "FAIL: 需要 envsubst(gettext)"; exit 1; }
|
||
|
||
secret_b64() {
|
||
local name="$1" key="$2"
|
||
kubectl -n "$NS" get secret "$name" -o "jsonpath={.data.${key}}" 2>/dev/null || true
|
||
}
|
||
|
||
load_secret() {
|
||
local var="$1" secret="$2" key="$3"
|
||
if [[ -n "${!var:-}" ]]; then
|
||
return 0
|
||
fi
|
||
local raw
|
||
raw="$(secret_b64 "$secret" "$key")"
|
||
if [[ -n "$raw" ]]; then
|
||
export "$var"="$(printf '%s' "$raw" | base64 -d)"
|
||
fi
|
||
}
|
||
|
||
echo "==> 加载密钥(K8s Secret / env)"
|
||
load_secret MYSQL_ROOT_PASSWORD prod-credentials MYSQL_ROOT_PASSWORD
|
||
load_secret REDIS_PASSWORD prod-credentials REDIS_PASSWORD
|
||
load_secret RABBITMQ_DEFAULT_PASS prod-credentials RABBITMQ_DEFAULT_PASS
|
||
load_secret ELASTIC_PASSWORD prod-credentials ELASTIC_PASSWORD
|
||
load_secret JWT_SECRET_KEY jwt-secret JWT_SECRET_KEY
|
||
# 兼容:JWT 也放在 prod-credentials
|
||
load_secret JWT_SECRET_KEY prod-credentials JWT_SECRET_KEY
|
||
|
||
: "${MYSQL_ROOT_PASSWORD:?缺少 MYSQL_ROOT_PASSWORD(env 或 secret/prod-credentials)}"
|
||
: "${REDIS_PASSWORD:?缺少 REDIS_PASSWORD}"
|
||
: "${RABBITMQ_DEFAULT_PASS:?缺少 RABBITMQ_DEFAULT_PASS}"
|
||
: "${ELASTIC_PASSWORD:?缺少 ELASTIC_PASSWORD}"
|
||
: "${JWT_SECRET_KEY:?缺少 JWT_SECRET_KEY(请创建 secret/jwt-secret 或写入 prod-credentials)}"
|
||
|
||
if [[ -z "$CONSUL_HTTP_ADDR" ]]; then
|
||
CIP="$(kubectl -n "$NS" get svc consul -o jsonpath='{.spec.clusterIP}')"
|
||
CONSUL_HTTP_ADDR="http://${CIP}:8500"
|
||
fi
|
||
echo "==> Consul ${CONSUL_HTTP_ADDR}"
|
||
|
||
# ACL(可选)
|
||
if [[ -z "${CONSUL_HTTP_TOKEN:-}" ]]; then
|
||
raw="$(secret_b64 consul-acl CONSUL_HTTP_TOKEN)"
|
||
if [[ -n "$raw" ]]; then
|
||
export CONSUL_HTTP_TOKEN="$(printf '%s' "$raw" | base64 -d)"
|
||
fi
|
||
fi
|
||
|
||
CURL_AUTH=()
|
||
if [[ -n "${CONSUL_HTTP_TOKEN:-}" ]]; then
|
||
CURL_AUTH=(-H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}")
|
||
fi
|
||
|
||
TARGETS=()
|
||
if [[ $# -eq 0 ]]; then
|
||
for f in "${IDWMS_DIR}"/*.json; do
|
||
[[ -f "$f" ]] || continue
|
||
TARGETS+=("$(basename "$f" .json)")
|
||
done
|
||
else
|
||
TARGETS=("$@")
|
||
fi
|
||
|
||
[[ ${#TARGETS[@]} -gt 0 ]] || { echo "FAIL: 无同步目标"; exit 1; }
|
||
|
||
TMP="$(mktemp -d)"
|
||
trap 'rm -rf "$TMP"' EXIT
|
||
|
||
put_kv() {
|
||
local key="$1" file="$2"
|
||
local code
|
||
code="$(curl -sS -o /tmp/consul-put.out -w '%{http_code}' \
|
||
"${CURL_AUTH[@]}" \
|
||
-X PUT --data-binary @"$file" \
|
||
"${CONSUL_HTTP_ADDR}/v1/kv/${key}")"
|
||
if [[ "$code" != "200" ]]; then
|
||
echo "FAIL: PUT ${key} HTTP ${code}"
|
||
cat /tmp/consul-put.out 2>/dev/null || true
|
||
exit 1
|
||
fi
|
||
echo "OK idwms/${key##*/} → ${key}"
|
||
}
|
||
|
||
RESTART_SET=()
|
||
for name in "${TARGETS[@]}"; do
|
||
src="${IDWMS_DIR}/${name}.json"
|
||
[[ -f "$src" ]] || { echo "FAIL: 无 ${src}"; exit 1; }
|
||
|
||
# 仅替换已知占位符,避免 JSON 里其它 $ 被误伤
|
||
export MYSQL_ROOT_PASSWORD REDIS_PASSWORD RABBITMQ_DEFAULT_PASS ELASTIC_PASSWORD JWT_SECRET_KEY
|
||
envsubst '${MYSQL_ROOT_PASSWORD} ${REDIS_PASSWORD} ${RABBITMQ_DEFAULT_PASS} ${ELASTIC_PASSWORD} ${JWT_SECRET_KEY}' \
|
||
<"$src" >"${TMP}/${name}.json"
|
||
|
||
if ! jq empty "${TMP}/${name}.json" 2>/dev/null; then
|
||
echo "FAIL: ${name}.json 渲染后不是合法 JSON"
|
||
exit 1
|
||
fi
|
||
|
||
# 禁止未替换的占位符漏网
|
||
if grep -qE '\$\{[A-Z0-9_]+\}' "${TMP}/${name}.json"; then
|
||
echo "FAIL: ${name}.json 仍有未替换占位符:"
|
||
grep -E '\$\{[A-Z0-9_]+\}' "${TMP}/${name}.json" || true
|
||
exit 1
|
||
fi
|
||
|
||
put_kv "idwms/${name}" "${TMP}/${name}.json"
|
||
|
||
case "$name" in
|
||
admin) RESTART_SET+=(admin-api) ;;
|
||
itfxapi) RESTART_SET+=(interfaceforward-api) ;;
|
||
trackapi) RESTART_SET+=(track-api) ;;
|
||
common)
|
||
RESTART_SET+=(admin-api interfaceforward-api track-api)
|
||
;;
|
||
esac
|
||
done
|
||
|
||
if [[ "$RESTART_APIS" == "1" ]]; then
|
||
# 去重
|
||
readarray -t RESTART_SET < <(printf '%s\n' "${RESTART_SET[@]}" | sort -u)
|
||
echo "==> rollout restart: ${RESTART_SET[*]}"
|
||
for d in "${RESTART_SET[@]}"; do
|
||
[[ -n "$d" ]] || continue
|
||
if kubectl -n "$NS" get "deploy/${d}" >/dev/null 2>&1; then
|
||
kubectl -n "$NS" rollout restart "deploy/${d}"
|
||
kubectl -n "$NS" rollout status "deploy/${d}" --timeout=180s || true
|
||
else
|
||
echo "SKIP deploy/${d}(不存在)"
|
||
fi
|
||
done
|
||
fi
|
||
|
||
echo "DONE"
|