#!/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"