#!/bin/bash set -e # Rollback Application Deployment # This script rolls back to a previous deployment using Ansible SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEPLOYMENT_DIR="$(dirname "$SCRIPT_DIR")" ANSIBLE_DIR="$DEPLOYMENT_DIR/ansible" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Function to print colored messages print_success() { echo -e "${GREEN}✅ $1${NC}" } print_error() { echo -e "${RED}❌ $1${NC}" } print_warning() { echo -e "${YELLOW}⚠️ $1${NC}" } print_info() { echo -e "${BLUE}ℹ️ $1${NC}" } # Show usage usage() { echo "Usage: $0 [version] [options]" echo "" echo "Arguments:" echo " version Backup version to rollback to (e.g., 2025-01-28T15-30-00)" echo " If not provided, will rollback to previous version" echo "" echo "Options:" echo " --list List available backup versions" echo " --dry-run Run in check mode without making changes" echo " --help Show this help message" echo "" echo "Examples:" echo " $0 # Rollback to previous version" echo " $0 2025-01-28T15-30-00 # Rollback to specific version" echo " $0 --list # List available backups" echo " $0 2025-01-28T15-30-00 --dry-run # Test rollback" exit 1 } # Parse arguments ROLLBACK_VERSION="" LIST_BACKUPS=false DRY_RUN="" while [[ $# -gt 0 ]]; do case $1 in --list) LIST_BACKUPS=true shift ;; --dry-run) DRY_RUN="--check" shift ;; --help) usage ;; *) if [ -z "$ROLLBACK_VERSION" ]; then ROLLBACK_VERSION="$1" shift else print_error "Unknown argument: $1" usage fi ;; esac done echo "" echo "🔄 Rollback Application Deployment" echo "===================================" echo "" # Check if running from correct directory if [ ! -f "$ANSIBLE_DIR/ansible.cfg" ]; then print_error "Error: Must run from deployment/scripts directory" exit 1 fi cd "$ANSIBLE_DIR" SSH_KEY="$HOME/.ssh/production" DEPLOY_USER="deploy" DEPLOY_HOST="94.16.110.151" # List available backups list_backups() { print_info "Fetching available backups from production server..." echo "" if ! ssh -i "$SSH_KEY" "$DEPLOY_USER@$DEPLOY_HOST" \ "ls -lt /home/deploy/backups/ 2>/dev/null" | tail -n +2; then print_error "Failed to list backups" print_info "Make sure backups exist on production server: /home/deploy/backups/" exit 1 fi echo "" print_info "To rollback to a specific version, run:" echo " $0 " echo "" print_info "To rollback to previous version, run:" echo " $0" exit 0 } # Check Prerequisites check_prerequisites() { echo "Checking Prerequisites..." echo "------------------------" # Check Ansible if ! command -v ansible &> /dev/null; then print_error "Ansible is not installed" exit 1 fi print_success "Ansible installed" # Check vault password if [ ! -f "$ANSIBLE_DIR/secrets/.vault_pass" ]; then print_error "Vault password file not found" exit 1 fi print_success "Vault password file found" # Check playbook if [ ! -f "$ANSIBLE_DIR/playbooks/rollback.yml" ]; then print_error "Rollback playbook not found" exit 1 fi print_success "Rollback playbook found" # Test connection print_info "Testing connection to production..." if ansible production -m ping > /dev/null 2>&1; then print_success "Connection successful" else print_error "Connection to production failed" exit 1 fi echo "" } # Get current deployment info get_current_deployment() { print_info "Fetching current deployment information..." CURRENT_IMAGE=$(ssh -i "$SSH_KEY" "$DEPLOY_USER@$DEPLOY_HOST" \ "docker service inspect app_app --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' 2>/dev/null" || echo "unknown") if [ "$CURRENT_IMAGE" != "unknown" ]; then print_info "Current deployment: $CURRENT_IMAGE" else print_warning "Could not determine current deployment" fi echo "" } # Main logic if [ "$LIST_BACKUPS" = true ]; then list_backups fi check_prerequisites get_current_deployment # Show available backups echo "Available Backups" echo "----------------" ssh -i "$SSH_KEY" "$DEPLOY_USER@$DEPLOY_HOST" \ "ls -lt /home/deploy/backups/ 2>/dev/null | tail -n +2 | head -10" || { print_warning "No backups found on production server" echo "" print_info "Backups are created automatically during deployments" print_info "You need at least one previous deployment to rollback" exit 1 } echo "" # Rollback Summary echo "Rollback Summary" echo "---------------" if [ -n "$ROLLBACK_VERSION" ]; then echo " Target Version: $ROLLBACK_VERSION" echo " Current Image: $CURRENT_IMAGE" else echo " Target Version: Previous deployment (most recent backup)" echo " Current Image: $CURRENT_IMAGE" fi if [ -n "$DRY_RUN" ]; then echo " Mode: DRY RUN (no changes will be made)" else echo " Mode: PRODUCTION ROLLBACK" fi echo "" # Confirmation if [ -z "$DRY_RUN" ]; then print_warning "⚠️ WARNING: This will rollback your production deployment!" echo "" read -p "Are you sure you want to proceed with rollback? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then print_warning "Rollback cancelled" exit 0 fi fi echo "" # Build ansible-playbook command ANSIBLE_CMD="ansible-playbook $ANSIBLE_DIR/playbooks/rollback.yml" ANSIBLE_CMD="$ANSIBLE_CMD --vault-password-file $ANSIBLE_DIR/secrets/.vault_pass" # Add version if specified if [ -n "$ROLLBACK_VERSION" ]; then ANSIBLE_CMD="$ANSIBLE_CMD -e rollback_to_version=$ROLLBACK_VERSION" fi # Add dry-run flag if set if [ -n "$DRY_RUN" ]; then ANSIBLE_CMD="$ANSIBLE_CMD $DRY_RUN" fi # Execute rollback print_info "Starting rollback..." echo "" if eval "$ANSIBLE_CMD"; then echo "" if [ -z "$DRY_RUN" ]; then print_success "Rollback completed successfully!" else print_success "Dry run completed successfully!" fi else echo "" print_error "Rollback failed!" echo "" print_info "Check the Ansible output above for error details" exit 1 fi echo "" # Post-rollback checks if [ -z "$DRY_RUN" ]; then echo "Post-Rollback Checks" echo "-------------------" # Check service status print_info "Checking service status..." ssh -i "$SSH_KEY" "$DEPLOY_USER@$DEPLOY_HOST" "docker service ls --filter name=app_" || true echo "" # Get new image NEW_IMAGE=$(ssh -i "$SSH_KEY" "$DEPLOY_USER@$DEPLOY_HOST" \ "docker service inspect app_app --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' 2>/dev/null" || echo "unknown") if [ "$NEW_IMAGE" != "unknown" ]; then print_info "Rolled back to: $NEW_IMAGE" fi echo "" # Show recent logs print_info "Recent application logs:" ssh -i "$SSH_KEY" "$DEPLOY_USER@$DEPLOY_HOST" "docker service logs --tail 20 app_app" || true echo "" # Summary echo "✅ Rollback Complete!" echo "====================" echo "" echo "Rollback Details:" echo " From: $CURRENT_IMAGE" echo " To: $NEW_IMAGE" if [ -n "$ROLLBACK_VERSION" ]; then echo " Version: $ROLLBACK_VERSION" else echo " Version: Previous deployment" fi echo "" echo "Next Steps:" echo "" echo "1. Monitor application:" echo " ssh -i $SSH_KEY $DEPLOY_USER@$DEPLOY_HOST 'docker service logs -f app_app'" echo "" echo "2. Check service status:" echo " ssh -i $SSH_KEY $DEPLOY_USER@$DEPLOY_HOST 'docker service ps app_app'" echo "" echo "3. Test application:" echo " curl https://michaelschiemer.de/health" echo "" echo "4. If rollback didn't fix the issue, check available backups:" echo " $0 --list" echo "" else echo "Dry Run Summary" echo "===============" echo "" echo "This was a dry run. No changes were made to production." echo "" if [ -n "$ROLLBACK_VERSION" ]; then echo "To rollback for real, run:" echo " $0 $ROLLBACK_VERSION" else echo "To rollback for real, run:" echo " $0" fi echo "" fi