All checks were successful
Update Automation Tests / Integration Tests (pull_request) Successful in 37s
- 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
252 lines
7.5 KiB
Bash
Executable File
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 "$@"
|