#!/usr/bin/env bash # ----------------------------------------------------------------------------- # redis-switch.sh — swap between local containers and HA cluster # (for Redis and RabbitMQ) # # Usage: # ./redis-switch.sh cluster # both to cluster (production) # ./redis-switch.sh local # both to local containers (fallback/dev) # ./redis-switch.sh status # show current active targets # ./redis-switch.sh cluster redis # only Redis to cluster # ./redis-switch.sh local rabbit # only RabbitMQ to local # # What it does: # 1. Writes *_HOST/PORT/USER/PASS/etc env vars to .env files # 2. For 'local': also starts the corresponding local container if stopped # 3. Restarts didi-framework + agent-v3 stack (both need env vars) # 4. Auto-bootstrap will populate Redis; RabbitMQ topology creates itself # # Safe to flip back and forth — data stays on both instances. # ----------------------------------------------------------------------------- set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MONOREPO_ROOT="$(cd "${SCRIPT_DIR}/../../../.." && pwd)" AGENT_DIR="${MONOREPO_ROOT}/backend/services/orchestration-layer/agent-v3" FW_DIR="${MONOREPO_ROOT}/backend/services/orchestration-layer/didiFramework" PROD_DIR="${MONOREPO_ROOT}/backend/production" DATA_DIR="${MONOREPO_ROOT}/backend/services/data-layer" # ------------------------------------------------------------------------- # Cluster credentials — loaded from sidecar file or shell env # ------------------------------------------------------------------------- # Shell env (DIDI_CLUSTER_PASSWORD) takes precedence. # Otherwise, load from `.cluster-credentials.env` in this directory. if [ -z "${DIDI_CLUSTER_PASSWORD:-}" ] && [ -f "${SCRIPT_DIR}/.cluster-credentials.env" ]; then # shellcheck disable=SC1090 set -a; source "${SCRIPT_DIR}/.cluster-credentials.env"; set +a fi CLUSTER_SHARED_PASSWORD="${DIDI_CLUSTER_PASSWORD:-}" # Redis cluster CLUSTER_REDIS_HOST="10.11.50.100" CLUSTER_REDIS_PORT="16379" CLUSTER_REDIS_USER="didi" CLUSTER_REDIS_PASSWORD="${CLUSTER_SHARED_PASSWORD}" CLUSTER_REDIS_DB="0" # RabbitMQ cluster CLUSTER_RABBIT_HOST="10.11.50.100" CLUSTER_RABBIT_PORT="16672" CLUSTER_RABBIT_USER="didi" CLUSTER_RABBIT_PASS="${CLUSTER_SHARED_PASSWORD}" CLUSTER_RABBIT_VHOST="/didi" # ------------------------------------------------------------------------- # Local config (fallback) # ------------------------------------------------------------------------- LOCAL_REDIS_HOST="didi-cache" LOCAL_REDIS_PORT="6379" LOCAL_REDIS_USER="" LOCAL_REDIS_PASSWORD="redis123" LOCAL_REDIS_DB="0" LOCAL_RABBIT_HOST="staging-dataLayer-rabbitmq" LOCAL_RABBIT_PORT="5672" LOCAL_RABBIT_USER="admin" LOCAL_RABBIT_PASS="rabbitmq123" LOCAL_RABBIT_VHOST="/" MODE="${1:-status}" TARGET="${2:-both}" # both | redis | rabbit # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- write_redis_block() { local env_file="$1" host="$2" port="$3" user="$4" pass="$5" db="$6" [ ! -f "$env_file" ] && touch "$env_file" local tmp="${env_file}.tmp" grep -vE '^(# Redis|REDIS_HOST=|REDIS_PORT=|REDIS_USERNAME=|REDIS_PASSWORD=|REDIS_DB=)' "$env_file" > "$tmp" || true { echo "" echo "# Redis (set by redis-switch.sh $(date -Iseconds))" echo "REDIS_HOST=${host}" echo "REDIS_PORT=${port}" echo "REDIS_USERNAME=${user}" echo "REDIS_PASSWORD=${pass}" echo "REDIS_DB=${db}" } >> "$tmp" mv "$tmp" "$env_file" echo " ✓ Redis env → ${env_file#${MONOREPO_ROOT}/}" } write_rabbit_block() { local env_file="$1" host="$2" port="$3" user="$4" pass="$5" vhost="$6" [ ! -f "$env_file" ] && touch "$env_file" local tmp="${env_file}.tmp" grep -vE '^(# RabbitMQ|RABBITMQ_HOST=|RABBITMQ_PORT=|RABBITMQ_USER=|RABBITMQ_PASS=|RABBITMQ_VHOST=)' "$env_file" > "$tmp" || true { echo "" echo "# RabbitMQ (set by redis-switch.sh $(date -Iseconds))" echo "RABBITMQ_HOST=${host}" echo "RABBITMQ_PORT=${port}" echo "RABBITMQ_USER=${user}" echo "RABBITMQ_PASS=${pass}" echo "RABBITMQ_VHOST=${vhost}" } >> "$tmp" mv "$tmp" "$env_file" echo " ✓ RabbitMQ env → ${env_file#${MONOREPO_ROOT}/}" } read_env() { local env="$1" key="$2" [ -f "$env" ] || { echo ""; return; } grep -E "^${key}=" "$env" | tail -1 | cut -d= -f2- } current_targets() { local env="${AGENT_DIR}/.env" local rh=$(read_env "$env" REDIS_HOST) local rp=$(read_env "$env" REDIS_PORT) local ru=$(read_env "$env" REDIS_USERNAME) local qh=$(read_env "$env" RABBITMQ_HOST) local qp=$(read_env "$env" RABBITMQ_PORT) local qv=$(read_env "$env" RABBITMQ_VHOST) echo " Redis → ${rh:-didi-cache}:${rp:-6379} (user: ${ru:-legacy})" echo " RabbitMQ → ${qh:-staging-dataLayer-rabbitmq}:${qp:-5672}${qv:-/}" } ensure_container_running() { local name="$1" compose_dir="$2" service="$3" if docker ps --filter "name=${name}" --filter "status=running" --format "{{.Names}}" | grep -q "${name}"; then echo " → ${name} already running" return fi if docker ps -a --filter "name=${name}" --format "{{.Names}}" | grep -q "${name}"; then echo " → Starting stopped ${name}..." docker start "${name}" > /dev/null else echo " → ${name} missing, recreating via compose..." (cd "${compose_dir}" && docker compose up -d "${service}" 2>&1 | tail -3) fi } restart_services() { echo "" echo "→ Restarting didi-framework..." (cd "$FW_DIR" && docker compose up -d --force-recreate 2>&1 | tail -3) echo "→ Restarting agent-v3 stack (API + 12 workers)..." (cd "$AGENT_DIR" && docker compose up -d --force-recreate 2>&1 | tail -3) echo "→ Waiting for services..." for i in $(seq 1 30); do if docker exec didi-framework wget -qO- http://127.0.0.1:3005/health > /dev/null 2>&1 \ && curl -sf http://localhost:24803/api/v3/health > /dev/null 2>&1; then echo " ✓ Both ready (${i}s)" return fi sleep 1 done echo " ⚠ Timeout waiting for health — check logs" } apply_redis_cluster() { write_redis_block "${AGENT_DIR}/.env" "$CLUSTER_REDIS_HOST" "$CLUSTER_REDIS_PORT" "$CLUSTER_REDIS_USER" "$CLUSTER_REDIS_PASSWORD" "$CLUSTER_REDIS_DB" write_redis_block "${FW_DIR}/.env" "$CLUSTER_REDIS_HOST" "$CLUSTER_REDIS_PORT" "$CLUSTER_REDIS_USER" "$CLUSTER_REDIS_PASSWORD" "$CLUSTER_REDIS_DB" } apply_redis_local() { ensure_container_running "didi-cache" "$PROD_DIR" "didi-cache" write_redis_block "${AGENT_DIR}/.env" "$LOCAL_REDIS_HOST" "$LOCAL_REDIS_PORT" "$LOCAL_REDIS_USER" "$LOCAL_REDIS_PASSWORD" "$LOCAL_REDIS_DB" write_redis_block "${FW_DIR}/.env" "$LOCAL_REDIS_HOST" "$LOCAL_REDIS_PORT" "$LOCAL_REDIS_USER" "$LOCAL_REDIS_PASSWORD" "$LOCAL_REDIS_DB" } apply_rabbit_cluster() { write_rabbit_block "${AGENT_DIR}/.env" "$CLUSTER_RABBIT_HOST" "$CLUSTER_RABBIT_PORT" "$CLUSTER_RABBIT_USER" "$CLUSTER_RABBIT_PASS" "$CLUSTER_RABBIT_VHOST" } apply_rabbit_local() { ensure_container_running "staging-dataLayer-rabbitmq" "$DATA_DIR" "staging-dataLayer-rabbitmq" write_rabbit_block "${AGENT_DIR}/.env" "$LOCAL_RABBIT_HOST" "$LOCAL_RABBIT_PORT" "$LOCAL_RABBIT_USER" "$LOCAL_RABBIT_PASS" "$LOCAL_RABBIT_VHOST" } # ------------------------------------------------------------------------- # Commands # ------------------------------------------------------------------------- case "$MODE" in status) echo "Current active targets (from agent-v3 .env):" current_targets echo "" echo "Local containers:" docker ps -a --filter "name=didi-cache" --format " didi-cache | {{.Status}}" | head -1 docker ps -a --filter "name=staging-dataLayer-rabbitmq" --format " staging-dataLayer-rabbitmq | {{.Status}}" | head -1 echo "" echo "Usage: $0 {cluster|local|status} [redis|rabbit|both]" ;; cluster) if [ -z "$CLUSTER_SHARED_PASSWORD" ]; then echo "✘ ERROR: cluster password not set." >&2 echo " Option A: export DIDI_CLUSTER_PASSWORD=..." >&2 echo " Option B: cp ${SCRIPT_DIR}/.cluster-credentials.env.example ${SCRIPT_DIR}/.cluster-credentials.env && edit it" >&2 exit 1 fi echo "═══ Switching to HA cluster (target: ${TARGET}) ═══" case "$TARGET" in both) apply_redis_cluster; apply_rabbit_cluster ;; redis) apply_redis_cluster ;; rabbit) apply_rabbit_cluster ;; *) echo "Unknown target: $TARGET (use: redis, rabbit, both)"; exit 1 ;; esac restart_services echo "" echo "✓ Cluster active. Bootstrap will auto-populate Redis on startup." ;; local) echo "═══ Switching to LOCAL containers (target: ${TARGET}) ═══" case "$TARGET" in both) apply_redis_local; apply_rabbit_local ;; redis) apply_redis_local ;; rabbit) apply_rabbit_local ;; *) echo "Unknown target: $TARGET (use: redis, rabbit, both)"; exit 1 ;; esac restart_services echo "" echo "✓ Local fallback active." ;; *) echo "Usage: $0 {cluster|local|status} [redis|rabbit|both]" exit 1 ;; esac