#!/bin/bash set -euo pipefail # ================================================================ # Restore script for mrt0rtikize.ru Matrix instance # ================================================================ # Reads a backup directory created by backup-mrt0rtikize.sh # and restores it to the new yandex-prod cluster. # # Usage: ./restore-mrt0rtikize.sh # # Steps: # 1. Scale Synapse + MAS to 0 on new cluster # 2. Restore PostgreSQL dumps to CNPG shared-pg # 3. Apply generated secrets (signing key, MAS keys, macaroon) # 4. Apply deployment markers # 5. Scale Synapse to 1 (now has signing key + DB) # 6. Restore media files # 7. Scale MAS to 1 # ================================================================ readonly YC_KUBECONFIG="${KUBECONFIG:-/home/mrt0rtikize/infra/yandex-prod/kubeconfig}" readonly K="${KUBECTL:-kubectl} --kubeconfig ${YC_KUBECONFIG}" readonly NS="matrix-mrt0rtikize" readonly CNPG_NS="cnpg" readonly CNPG_POD="shared-pg-1" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[$(date +%H:%M:%S)]${NC} $*"; } warn() { echo -e "${YELLOW}[$(date +%H:%M:%S)] WARN:${NC} $*"; } err() { echo -e "${RED}[$(date +%H:%M:%S)] ERROR:${NC} $*"; } # ------------------------------------------------------------------- # Usage # ------------------------------------------------------------------- if [ $# -ne 1 ]; then echo "Usage: $0 " echo "" echo "Example: $0 backups/matrix-mrt0rtikize-20260613-192010/" exit 1 fi readonly BACKUP_DIR="$1" if [ ! -d "${BACKUP_DIR}" ]; then err "Backup directory not found: ${BACKUP_DIR}" exit 1 fi # Verify critical files exist for f in dump-synapse.sql dump-mas.sql secret-generated.yaml markers.yaml synapse-media.tar.gz; do if [ ! -f "${BACKUP_DIR}/${f}" ]; then warn "Missing: ${f}" fi done # ------------------------------------------------------------------- # Prerequisites # ------------------------------------------------------------------- log "=== Checking prerequisites ===" if ! ${K} get ns "${NS}" >/dev/null 2>&1; then err "Namespace ${NS} not found on new cluster. Deploy the ESS app first." exit 1 fi if ! ${K} -n "${CNPG_NS}" get pod "${CNPG_POD}" >/dev/null 2>&1; then err "CNPG pod ${CNPG_POD} not found. Is the CNPG cluster running?" exit 1 fi log "Cluster access verified." # ------------------------------------------------------------------- # Step 1: Scale Synapse + MAS to 0 # ------------------------------------------------------------------- log "=== Step 1: Stopping Synapse + MAS ===" ${K} -n "${NS}" scale sts -l "app.kubernetes.io/component=matrix-server" --replicas=0 2>/dev/null || \ ${K} -n "${NS}" scale sts "${NS}-synapse-main" --replicas=0 2>/dev/null || \ warn "Could not scale Synapse via known names, trying by label..." ${K} -n "${NS}" scale deploy -l "app.kubernetes.io/component=matrix-authentication" --replicas=0 2>/dev/null || \ ${K} -n "${NS}" scale deploy "${NS}-matrix-authentication-service" --replicas=0 2>/dev/null || \ warn "Could not scale MAS via known names..." log "Waiting for Synapse + MAS pods to terminate..." ${K} -n "${NS}" wait --for=delete pod -l "app.kubernetes.io/component=matrix-server" --timeout=120s 2>/dev/null || warn "Some Synapse pods may still be terminating." ${K} -n "${NS}" wait --for=delete pod -l "app.kubernetes.io/component=matrix-authentication" --timeout=120s 2>/dev/null || warn "Some MAS pods may still be terminating." log "Synapse + MAS stopped." # ------------------------------------------------------------------- # Step 2: Clean schemas (DROP SCHEMA CASCADE — no connection races) # ------------------------------------------------------------------- log "=== Step 2: Cleaning database schemas ===" log "Reading PG credentials from cluster..." SYNAPSE_PW=$(${K} get secret pg-creds -n "${NS}" -o jsonpath='{.data.synapse}' | base64 -d) MAS_PW=$(${K} get secret pg-creds -n "${NS}" -o jsonpath='{.data.mas}' | base64 -d) log "Wiping synapse schema..." ${K} exec -n "${CNPG_NS}" "${CNPG_POD}" -- env PGPASSWORD="${SYNAPSE_PW}" \ psql -U synapse_mrt0rtikize -d synapse_mrt0rtikize -h localhost -c \ "DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO synapse_mrt0rtikize;" 2>/dev/null || true log "Wiping MAS schema..." ${K} exec -n "${CNPG_NS}" "${CNPG_POD}" -- env PGPASSWORD="${MAS_PW}" \ psql -U mas_mrt0rtikize -d mas_mrt0rtikize -h localhost -c \ "DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO mas_mrt0rtikize;" 2>/dev/null || true log "Schemas cleaned." # ------------------------------------------------------------------- # Step 3: Restore PostgreSQL dumps # ------------------------------------------------------------------- log "=== Step 3: Restoring PostgreSQL dumps ===" log "Restoring Synapse database..." ${K} exec -i -n "${CNPG_NS}" "${CNPG_POD}" -- env PGPASSWORD="${SYNAPSE_PW}" \ psql -U synapse_mrt0rtikize -d synapse_mrt0rtikize -h localhost < "${BACKUP_DIR}/dump-synapse.sql" log "Synapse database restored." log "Restoring MAS database..." ${K} exec -i -n "${CNPG_NS}" "${CNPG_POD}" -- env PGPASSWORD="${MAS_PW}" \ psql -U mas_mrt0rtikize -d mas_mrt0rtikize -h localhost < "${BACKUP_DIR}/dump-mas.sql" log "MAS database restored." # ------------------------------------------------------------------- # Step 3: Apply generated secrets (CRITICAL) # ------------------------------------------------------------------- log "=== Step 4: Applying generated secrets ===" if [ -f "${BACKUP_DIR}/secret-generated.yaml" ]; then ${K} replace --force -f "${BACKUP_DIR}/secret-generated.yaml" log "Generated secret replaced: ${NS}-generated (original signing key from backup)" else err "CRITICAL: secret-generated.yaml not found in backup!" err "SYNAPSE_SIGNING_KEY and MAS keys will NOT be restored." err "Federation identity is lost without this file." fi # ------------------------------------------------------------------- # Step 4: Apply deployment markers # ------------------------------------------------------------------- log "=== Step 5: Applying deployment markers ===" if [ -f "${BACKUP_DIR}/markers.yaml" ]; then ${K} replace --force -f "${BACKUP_DIR}/markers.yaml" log "Deployment markers replaced." else warn "markers.yaml not found in backup (non-critical)." fi # ------------------------------------------------------------------- # Step 5: Scale Synapse to 1 # ------------------------------------------------------------------- log "=== Step 6: Starting Synapse ===" ${K} -n "${NS}" scale sts -l "app.kubernetes.io/component=matrix-server" --replicas=1 2>/dev/null || \ ${K} -n "${NS}" scale sts "${NS}-synapse-main" --replicas=1 2>/dev/null log "Waiting for Synapse to start..." ${K} -n "${NS}" wait --for=condition=ready pod -l "app.kubernetes.io/component=matrix-server" --timeout=300s 2>/dev/null || warn "Synapse is not ready yet, check logs." # ------------------------------------------------------------------- # Step 6: Restore media files # ------------------------------------------------------------------- log "=== Step 7: Restoring media files ===" if [ -f "${BACKUP_DIR}/synapse-media.tar.gz" ]; then SYNAPSE_POD=$( ${K} -n "${NS}" get pods -l "app.kubernetes.io/component=matrix-server" -o jsonpath='{.items[?(@.status.phase=="Running")].metadata.name}' 2>/dev/null) if [ -z "${SYNAPSE_POD}" ]; then warn "No running Synapse pod found for media restore. Skip media." warn "Re-run this step after Synapse is up:" warn " kubectl cp synapse-media.tar.gz ${NS}/:/tmp/ && kubectl exec -- tar xzf /tmp/synapse-media.tar.gz -C /media/" else log "Copying media to Synapse pod: ${SYNAPSE_POD}" ${K} cp "${BACKUP_DIR}/synapse-media.tar.gz" "${NS}/${SYNAPSE_POD}:/tmp/synapse-media.tar.gz" ${K} exec -n "${NS}" "${SYNAPSE_POD}" -- tar xzf /tmp/synapse-media.tar.gz -C /media/ ${K} exec -n "${NS}" "${SYNAPSE_POD}" -- rm /tmp/synapse-media.tar.gz log "Media files restored." fi else warn "synapse-media.tar.gz not found in backup." fi # ------------------------------------------------------------------- # Step 7: Scale MAS to 1 # ------------------------------------------------------------------- log "=== Step 8: Starting MAS ===" ${K} -n "${NS}" scale deploy -l "app.kubernetes.io/component=matrix-authentication" --replicas=1 2>/dev/null || \ ${K} -n "${NS}" scale deploy "${NS}-matrix-authentication-service" --replicas=1 2>/dev/null log "Waiting for MAS to start..." ${K} -n "${NS}" wait --for=condition=ready pod -l "app.kubernetes.io/component=matrix-authentication" --timeout=120s 2>/dev/null || warn "MAS is not ready yet, check logs." # ------------------------------------------------------------------- # Summary # ------------------------------------------------------------------- cat <