#!/bin/bash # # Emergency Rollback Script # Purpose: Fast rollback with minimal user interaction # # Usage: # ./scripts/emergency-rollback.sh # Interactive mode # ./scripts/emergency-rollback.sh # Direct rollback # ./scripts/emergency-rollback.sh list # List available tags # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" ANSIBLE_DIR="${PROJECT_ROOT}/deployment/ansible" INVENTORY="${ANSIBLE_DIR}/inventory/production.yml" PRODUCTION_SERVER="94.16.110.151" REGISTRY="git.michaelschiemer.de:5000" IMAGE="framework" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_info() { echo -e "${GREEN}[INFO]${NC} $1" } # List available image tags list_tags() { log_info "Fetching available image tags from production..." ssh -i ~/.ssh/production deploy@"${PRODUCTION_SERVER}" \ "docker images ${REGISTRY}/${IMAGE} --format '{{.Tag}}' | grep -v buildcache | head -20" echo "" log_info "Current running version:" ssh -i ~/.ssh/production deploy@"${PRODUCTION_SERVER}" \ "docker service inspect framework_web --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}'" } # Get current image tag get_current_tag() { ssh -i ~/.ssh/production deploy@"${PRODUCTION_SERVER}" \ "docker service inspect framework_web --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' | cut -d':' -f2" } # Emergency rollback emergency_rollback() { local target_tag="$1" echo "" log_warn "╔════════════════════════════════════════════════════════╗" log_warn "║ 🚨 EMERGENCY ROLLBACK INITIATED 🚨 ║" log_warn "╚════════════════════════════════════════════════════════╝" echo "" local current_tag=$(get_current_tag) echo "Current Version: ${current_tag}" echo "Target Version: ${target_tag}" echo "" if [[ "${current_tag}" == "${target_tag}" ]]; then log_warn "Already running ${target_tag}. No rollback needed." exit 0 fi log_warn "This will immediately rollback production WITHOUT health checks." log_warn "Use only in emergency situations." echo "" read -p "Type 'ROLLBACK' to confirm: " -r if [[ ! "$REPLY" == "ROLLBACK" ]]; then log_info "Rollback cancelled" exit 0 fi log_info "Executing emergency rollback via Ansible..." cd "${ANSIBLE_DIR}" ansible-playbook \ -i "${INVENTORY}" \ playbooks/emergency-rollback.yml \ -e "rollback_tag=${target_tag}" echo "" log_warn "╔════════════════════════════════════════════════════════╗" log_warn "║ MANUAL VERIFICATION REQUIRED ║" log_warn "╚════════════════════════════════════════════════════════╝" echo "" log_warn "1. Check application: https://michaelschiemer.de" log_warn "2. Run health check: cd deployment && ansible-playbook -i ansible/inventory/production.yml ansible/playbooks/health-check.yml" log_warn "3. Check service logs: ssh deploy@${PRODUCTION_SERVER} 'docker service logs framework_web --tail 100'" echo "" } # Interactive mode interactive_rollback() { log_info "🚨 Emergency Rollback - Interactive Mode" echo "" log_info "Available image tags (last 20):" list_tags echo "" read -p "Enter image tag to rollback to: " -r target_tag if [[ -z "$target_tag" ]]; then log_error "No tag provided" exit 1 fi emergency_rollback "$target_tag" } # Main main() { case "${1:-interactive}" in list) list_tags ;; interactive) interactive_rollback ;; help|--help|-h) cat < Direct rollback to specific tag help Show this help Examples: $0 list # List available versions $0 # Interactive mode $0 abc1234-123456 # Rollback to specific tag Emergency Procedures: 1. List versions: $0 list 2. Choose version: $0 3. Verify manually: https://michaelschiemer.de 4. Run health check: cd deployment && ansible-playbook -i ansible/inventory/production.yml ansible/playbooks/health-check.yml EOF ;; *) # Direct rollback with provided tag emergency_rollback "$1" ;; esac } main "$@"