qvest-task/scripts/auto-update.sh
gitea_admin 2e368a3a7c feat: implement disaster recovery with automated restore (#2)
- 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

Co-authored-by: aviyadeveloper <aviya.developer@gmail.com>
Reviewed-on: #2
2026-06-11 17:29:55 +00:00

274 lines
8.1 KiB
Bash

#!/bin/bash
# ============================================================================
# Gitea Auto-Update Script
# ============================================================================
# Automatically updates low-risk containers (nginx, certbot) with backup,
# health checks, and automatic rollback on failure.
#
# Usage: ./auto-update.sh <container1> [container2] [...]
# Example: ./auto-update.sh nginx certbot
# ============================================================================
set -e
# ============================================================================
# Configuration
# ============================================================================
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly DOCKER_COMPOSE_DIR="/opt/gitea"
readonly COMPOSE_FILE="${DOCKER_COMPOSE_DIR}/docker-compose.yml"
readonly BACKUP_SCRIPT="${SCRIPT_DIR}/backup.sh"
readonly HEALTH_CHECK_SCRIPT="${SCRIPT_DIR}/health-check.sh"
readonly LOG_FILE="/var/log/gitea-auto-update.log"
readonly ROLLBACK_INFO="/tmp/gitea-rollback-info-$$.json"
# Wait timeouts (seconds)
readonly CONTAINER_STARTUP_WAIT=10
# Output colors
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly RED='\033[0;31m'
readonly NC='\033[0m'
# ============================================================================
# Logging Functions
# ============================================================================
get_timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
log_info() {
local message="[$(get_timestamp)] [INFO] $1"
echo -e "${YELLOW}${message}${NC}"
echo "${message}" >> "${LOG_FILE}"
}
log_success() {
local message="[$(get_timestamp)] [SUCCESS] $1"
echo -e "${GREEN}${message}${NC}"
echo "${message}" >> "${LOG_FILE}"
}
log_error() {
local message="[$(get_timestamp)] [ERROR] $1"
echo -e "${RED}${message}${NC}" >&2
echo "${message}" >> "${LOG_FILE}"
}
error_exit() {
log_error "$1"
cleanup
exit 1
}
# ============================================================================
# Cleanup Function
# ============================================================================
cleanup() {
if [ -f "${ROLLBACK_INFO}" ]; then
rm -f "${ROLLBACK_INFO}"
fi
}
# ============================================================================
# Helper Functions
# ============================================================================
change_to_compose_dir() {
cd "${DOCKER_COMPOSE_DIR}" || error_exit "Failed to change to ${DOCKER_COMPOSE_DIR}"
}
run_compose() {
docker compose -f "${COMPOSE_FILE}" "$@"
}
# ============================================================================
# Validation Functions
# ============================================================================
validate_args() {
if [ $# -eq 0 ]; then
error_exit "No containers specified. Usage: $0 <container1> [container2] [...]"
fi
for container in "$@"; do
if ! run_compose config --services | grep -q "^${container}$"; then
error_exit "Container '${container}' not found in docker-compose.yml"
fi
done
log_success "Container validation passed"
}
# ============================================================================
# Rollback Management Functions
# ============================================================================
save_current_images() {
log_info "Saving current image versions for rollback..."
echo "{" > "${ROLLBACK_INFO}"
local first=true
for container in "$@"; do
local image=$(run_compose images -q "${container}" 2>/dev/null | head -n1)
if [ -n "${image}" ]; then
if [ "${first}" = true ]; then
first=false
else
echo "," >> "${ROLLBACK_INFO}"
fi
echo " \"${container}\": \"${image}\"" >> "${ROLLBACK_INFO}"
log_info "Saved ${container}: ${image}"
fi
done
echo "}" >> "${ROLLBACK_INFO}"
log_success "Current image versions saved"
}
rollback() {
log_error "Rolling back to previous versions..."
if [ ! -f "${ROLLBACK_INFO}" ]; then
log_error "No rollback information found"
return 1
fi
change_to_compose_dir
# Extract containers from rollback info and restore
local containers=$(grep -o '"[^"]*":' "${ROLLBACK_INFO}" | tr -d '":' | tr '\n' ' ')
for container in ${containers}; do
log_info "Rolling back ${container}..."
run_compose up -d "${container}" || log_error "Failed to rollback ${container}"
done
log_success "Rollback completed"
}
# ============================================================================
# Update Functions
# ============================================================================
run_backup() {
log_info "Running backup before update..."
if ! bash "${BACKUP_SCRIPT}"; then
error_exit "Backup failed - aborting update"
fi
log_success "Backup completed successfully"
}
pull_new_images() {
log_info "Pulling new images..."
change_to_compose_dir
for container in "$@"; do
log_info "Pulling image for ${container}..."
if ! run_compose pull "${container}"; then
error_exit "Failed to pull image for ${container}"
fi
done
log_success "All images pulled successfully"
}
recreate_containers() {
log_info "Recreating containers..."
change_to_compose_dir
if ! run_compose up -d "$@"; then
error_exit "Failed to recreate containers"
fi
# Wait for containers to start
log_info "Waiting for containers to start..."
sleep "${CONTAINER_STARTUP_WAIT}"
log_success "Containers recreated successfully"
}
run_health_check() {
log_info "Running health check..."
if bash "${HEALTH_CHECK_SCRIPT}"; then
log_success "Health check passed"
return 0
else
log_error "Health check failed"
return 1
fi
}
send_failure_notification() {
local subject="$1"
local body="$2"
# Placeholder for email notification
# Will be configured with proper email settings in Task 6
log_error "NOTIFICATION: ${subject}"
log_error "${body}"
# TODO: Implement actual email sending via mail command or SMTP
# echo "${body}" | mail -s "${subject}" admin@example.com
}
# ============================================================================
# Main Execution
# ============================================================================
main() {
log_info "=========================================="
log_info "Gitea Auto-Update Started"
log_info "Containers: $*"
log_info "=========================================="
# Validate input
validate_args "$@"
# Save current state for rollback
save_current_images "$@"
# Run backup
run_backup
# Pull new images
pull_new_images "$@"
# Recreate containers
recreate_containers "$@"
# Run health check
if run_health_check; then
log_success "=========================================="
log_success "Update completed successfully"
log_success "Updated containers: $*"
log_success "=========================================="
cleanup
exit 0
else
log_error "Health check failed after update"
rollback
# Run health check again after rollback
if run_health_check; then
log_success "Rollback successful - services restored"
send_failure_notification \
"Gitea Update Failed - Rolled Back" \
"Update of containers [$*] failed health check and was rolled back. Services are now healthy."
else
log_error "Critical: Services still unhealthy after rollback"
send_failure_notification \
"CRITICAL: Gitea Update Failed - Manual Intervention Required" \
"Update of containers [$*] failed and rollback did not restore health. IMMEDIATE ATTENTION REQUIRED."
fi
cleanup
exit 1
fi
}
main "$@"