config/scripts/sync-to-consul.sh

159 lines
4.8 KiB
Bash
Raw 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
# 将仓库 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"