qvest-task/scripts/restore.sh
aviyadeveloper b8eb8e991c
All checks were successful
Update Automation Tests / Integration Tests (pull_request) Successful in 37s
feat: implement disaster recovery with automated restore
- Create restore.sh for automated S3 backup recovery
  - Fetches backups, stops services, restores database/data/config, restarts & validates
- Successfully tested on production system
- Document procedures in backup-strategy.md
- Add Test 6: Full backup/restore cycle with disaster simulation
- Rename test-update.sh → test-integration.sh
2026-06-11 19:27:49 +02:00

252 lines
7.5 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# Gitea Restore Script
# ============================================================================
# Restores PostgreSQL database, Gitea data, and configuration from S3 backups
#
# Usage: ./restore.sh <timestamp>
# Example: ./restore.sh 20260611_140530
#
# This will restore backups with the specified timestamp from S3
# ============================================================================
set -e
# ============================================================================
# Configuration
# ============================================================================
readonly S3_BUCKET="qvest-task-backups"
readonly S3_PREFIX="backups"
readonly RESTORE_DIR="/tmp/gitea-restore"
readonly LOG_FILE="/var/log/gitea-restore.log"
readonly DB_CONTAINER="gitea-postgres"
readonly DB_USER="gitea"
readonly DB_NAME="gitea"
readonly DATA_VOLUME="gitea_gitea-data"
readonly CONFIG_DIR="/opt/gitea"
# Output colors
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly RED='\033[0;31m'
readonly NC='\033[0m'
# ============================================================================
# Logging Functions
# ============================================================================
log_info() {
echo -e "${YELLOW}[INFO]${NC} $1" | tee -a "${LOG_FILE}"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "${LOG_FILE}"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" | tee -a "${LOG_FILE}" >&2
}
error_exit() {
log_error "$1"
cleanup
exit 1
}
# ============================================================================
# Validation Functions
# ============================================================================
validate_timestamp() {
if [[ ! "$1" =~ ^[0-9]{8}_[0-9]{6}$ ]]; then
error_exit "Invalid timestamp format. Expected: YYYYMMDD_HHMMSS"
fi
}
check_s3_backup_exists() {
local timestamp="$1"
local file="$2"
if ! aws s3 ls "s3://${S3_BUCKET}/${S3_PREFIX}/${file}-${timestamp}.tar.gz" &>/dev/null && \
! aws s3 ls "s3://${S3_BUCKET}/${S3_PREFIX}/${file}-${timestamp}.sql.gz" &>/dev/null; then
return 1
fi
return 0
}
# ============================================================================
# Core Functions
# ============================================================================
cleanup() {
if [ -d "${RESTORE_DIR}" ]; then
rm -rf "${RESTORE_DIR}"
fi
}
create_restore_dir() {
mkdir -p "${RESTORE_DIR}" || error_exit "Failed to create restore directory"
}
download_backups() {
local timestamp="$1"
log_info "Downloading backups from S3..."
aws s3 cp "s3://${S3_BUCKET}/${S3_PREFIX}/database-${timestamp}.sql.gz" \
"${RESTORE_DIR}/" || error_exit "Failed to download database backup"
aws s3 cp "s3://${S3_BUCKET}/${S3_PREFIX}/gitea-data-${timestamp}.tar.gz" \
"${RESTORE_DIR}/" || error_exit "Failed to download Gitea data backup"
aws s3 cp "s3://${S3_BUCKET}/${S3_PREFIX}/config-${timestamp}.tar.gz" \
"${RESTORE_DIR}/" || error_exit "Failed to download configuration backup"
log_success "Backups downloaded successfully"
}
stop_services() {
log_info "Stopping Gitea services..."
cd "${CONFIG_DIR}" || error_exit "Failed to change to config directory"
docker compose stop gitea || error_exit "Failed to stop Gitea"
log_success "Services stopped"
}
restore_database() {
local timestamp="$1"
log_info "Restoring database..."
# Drop and recreate database
docker exec "${DB_CONTAINER}" psql -U "${DB_USER}" -d postgres \
-c "DROP DATABASE IF EXISTS ${DB_NAME};" || error_exit "Failed to drop database"
docker exec "${DB_CONTAINER}" psql -U "${DB_USER}" -d postgres \
-c "CREATE DATABASE ${DB_NAME};" || error_exit "Failed to create database"
# Restore from backup
gunzip -c "${RESTORE_DIR}/database-${timestamp}.sql.gz" | \
docker exec -i "${DB_CONTAINER}" psql -U "${DB_USER}" -d "${DB_NAME}" \
|| error_exit "Failed to restore database"
log_success "Database restored"
}
restore_gitea_data() {
local timestamp="$1"
log_info "Restoring Gitea data..."
# Clear existing data
docker run --rm \
-v "${DATA_VOLUME}:/data" \
alpine sh -c "rm -rf /data/*" \
|| error_exit "Failed to clear Gitea data"
# Restore from backup
docker run --rm \
-v "${DATA_VOLUME}:/data" \
-v "${RESTORE_DIR}:/backup:ro" \
alpine tar xzf "/backup/gitea-data-${timestamp}.tar.gz" -C /data \
|| error_exit "Failed to restore Gitea data"
log_success "Gitea data restored"
}
restore_configuration() {
local timestamp="$1"
log_info "Restoring configuration files..."
# Extract configuration backup
tar xzf "${RESTORE_DIR}/config-${timestamp}.tar.gz" -C "${CONFIG_DIR}" \
|| error_exit "Failed to restore configuration"
log_success "Configuration restored"
}
start_services() {
log_info "Starting Gitea services..."
cd "${CONFIG_DIR}" || error_exit "Failed to change to config directory"
docker compose up -d || error_exit "Failed to start services"
log_info "Waiting for services to be ready..."
sleep 10
log_success "Services started"
}
verify_restore() {
log_info "Verifying restore..."
# Check if Gitea is responding
if curl -f -s http://localhost:3000 > /dev/null; then
log_success "Gitea is responding"
else
log_error "Gitea is not responding - manual verification required"
fi
# Check database connection
if docker exec "${DB_CONTAINER}" psql -U "${DB_USER}" -d "${DB_NAME}" \
-c "SELECT 1 FROM public.user LIMIT 1;" &>/dev/null; then
log_success "Database is accessible"
else
log_error "Database verification failed"
fi
}
# ============================================================================
# Main Execution
# ============================================================================
main() {
if [ $# -ne 1 ]; then
echo "Usage: $0 <timestamp>"
echo " Example: $0 20260611_140530"
echo ""
echo "Available backups:"
aws s3 ls "s3://${S3_BUCKET}/${S3_PREFIX}/" | grep "database-" | \
sed 's/.*database-\([0-9_]*\)\.sql\.gz/ \1/' | sort -u
exit 1
fi
local timestamp="$1"
log_info "Starting restore process for timestamp: ${timestamp}"
validate_timestamp "${timestamp}"
if ! check_s3_backup_exists "${timestamp}" "database"; then
error_exit "Backup with timestamp ${timestamp} not found in S3"
fi
# Confirm restore
echo ""
log_error "WARNING: This will replace all current data!"
read -p "Are you sure you want to continue? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Restore cancelled"
exit 0
fi
create_restore_dir
download_backups "${timestamp}"
stop_services
restore_database "${timestamp}"
restore_gitea_data "${timestamp}"
restore_configuration "${timestamp}"
start_services
verify_restore
cleanup
log_success "Restore completed successfully"
echo ""
log_info "Please verify the system is functioning correctly:"
log_info " 1. Access https://git.poll-streams.com"
log_info " 2. Login with your credentials"
log_info " 3. Verify repositories are accessible"
log_info " 4. Check system settings"
}
main "$@"