# VAPORA Woodpecker Pipeline - Rollback Deployment # Safe deployment rollback with verification and pre-checks # Triggers on: manual promotion only (safety feature) trigger: event: [promote] branch: [main, develop] variables: ARTIFACTS_DIR: provisioning/artifacts LOGS_DIR: provisioning/logs VAPORA_NAMESPACE: vapora stages: pre_rollback_checks: steps: - name: verify_environment image: alpine:latest commands: - | echo "šŸ”’ Pre-Rollback Safety Checks" echo "---" mkdir -p ${LOGS_DIR}/rollback { echo "Rollback initiated at: $(date -u +'%Y-%m-%dT%H:%M:%SZ')" echo "Commit: ${CI_COMMIT_SHA:0:8}" echo "Branch: ${CI_COMMIT_BRANCH}" echo "Pipeline: ${CI_BUILD_LINK}" echo "" echo "āš ļø This action will rollback production systems!" echo " Ensure this is intentional and approved." } | tee ${LOGS_DIR}/rollback/pre-rollback-snapshot.txt install_dependencies: depends_on: [pre_rollback_checks] steps: - name: install_tools image: rust:latest commands: - apt-get update && apt-get install -y curl jq yq - cargo install nu --locked - pip install jinja2-cli - curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" - chmod +x kubectl && mv kubectl /usr/local/bin/ - nu --version - kubectl version --client - yq --version configure_kubernetes: depends_on: [install_dependencies] steps: - name: setup_kubeconfig image: alpine:latest environment: KUBE_CONFIG_STAGING: ${KUBE_CONFIG_STAGING} KUBE_CONFIG_PRODUCTION: ${KUBE_CONFIG_PRODUCTION} commands: - mkdir -p ~/.kube - | if [ "${Rollback_Environment}" = "production" ]; then echo "$KUBE_CONFIG_PRODUCTION" | base64 -d > ~/.kube/config echo "āœ“ Production kubeconfig configured" else echo "$KUBE_CONFIG_STAGING" | base64 -d > ~/.kube/config echo "āœ“ Staging kubeconfig configured" fi - chmod 600 ~/.kube/config - kubectl cluster-info - kubectl get nodes store_deployment_history: depends_on: [configure_kubernetes] steps: - name: snapshot_current_state image: alpine:latest commands: - apk add --no-cache curl - | echo "šŸ“ø Storing current deployment history..." mkdir -p ${LOGS_DIR}/rollback { echo "=== Current Deployment State ===" | tee ${LOGS_DIR}/rollback/pre-rollback-status.txt echo "" echo "Deployments:" kubectl get deployments -n ${VAPORA_NAMESPACE} -o yaml | tee -a ${LOGS_DIR}/rollback/pre-rollback-status.txt echo "" echo "Rollout History:" for deployment in vapora-backend vapora-agents vapora-llm-router; do echo "--- $deployment ---" | tee -a ${LOGS_DIR}/rollback/pre-rollback-status.txt kubectl rollout history deployment/$deployment -n ${VAPORA_NAMESPACE} 2>&1 | tee -a ${LOGS_DIR}/rollback/pre-rollback-status.txt done } kubernetes_rollback: depends_on: [store_deployment_history] steps: - name: perform_rollback image: rust:latest environment: RUST_LOG: warn commands: - apt-get update && apt-get install -y curl jq - | echo "šŸ”™ Performing Kubernetes Rollback..." mkdir -p ${LOGS_DIR}/rollback cd provisioning nu scripts/rollback.nu \ --target kubernetes \ --deployment "${Rollback_Deployment:-all}" \ --revision ${Rollback_Revision:-0} \ 2>&1 | tee ../${LOGS_DIR}/rollback/rollback-output.log - name: verify_rollback image: alpine:latest commands: - apk add --no-cache curl - | echo "āœ“ Verifying rollback status..." { echo "=== Post-Rollback Deployment State ===" | tee ${LOGS_DIR}/rollback/post-rollback-status.txt echo "" echo "Deployments:" kubectl get deployments -n ${VAPORA_NAMESPACE} -o wide | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt echo "" echo "Rollout Status:" for deployment in vapora-backend vapora-agents vapora-llm-router; do echo "--- $deployment ---" | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt kubectl rollout status deployment/$deployment -n ${VAPORA_NAMESPACE} --timeout=5m 2>&1 | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt done } - name: check_pod_health image: alpine:latest commands: - apk add --no-cache curl - | echo "Pod Status After Rollback:" | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt kubectl get pods -n ${VAPORA_NAMESPACE} -o wide | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt echo "" | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt echo "Recent Events:" | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt kubectl get events -n ${VAPORA_NAMESPACE} --sort-by='.lastTimestamp' | tail -20 | tee -a ${LOGS_DIR}/rollback/post-rollback-status.txt docker_rollback_guide: depends_on: [store_deployment_history] steps: - name: generate_docker_guide image: alpine:latest commands: - | echo "šŸ“ Generating Docker rollback guide..." mkdir -p ${LOGS_DIR}/rollback cat > ${LOGS_DIR}/rollback/DOCKER_ROLLBACK_GUIDE.md << 'EOF' # Docker Rollback Guide Docker Compose rollback requires manual steps: ## Option 1: Revert to previous compose file ```bash cd deploy/docker docker compose down git checkout HEAD~1 docker-compose.yml docker compose up -d ``` ## Option 2: Stop and restart with older images ```bash docker compose -f docker-compose.yml.backup up -d ``` ## Option 3: Remove containers and redeploy from previous artifacts ```bash docker compose down docker system prune -f docker compose up -d ``` ## Verification After rollback, verify services are running: ```bash docker compose ps docker compose logs -f backend curl http://localhost:8001/health ``` ## Checking Compose File Backups ```bash find . -name "docker-compose*.yml*" -type f | sort ``` ## Restoring from Backup ```bash # If you have a timestamped backup cp docker-compose.yml.$(date +%s) docker-compose.yml docker compose up -d ``` EOF cat ${LOGS_DIR}/rollback/DOCKER_ROLLBACK_GUIDE.md - name: store_docker_state image: alpine:latest commands: - | echo "šŸ“‹ Storing Docker Compose state..." mkdir -p ${LOGS_DIR}/rollback if [ -f "deploy/docker/docker-compose.yml" ]; then cp deploy/docker/docker-compose.yml ${LOGS_DIR}/rollback/current-docker-compose.yml echo "āœ“ Current docker-compose.yml backed up" fi echo "Looking for available backups..." find . -name "docker-compose*.yml*" -type f 2>/dev/null | head -20 | tee ${LOGS_DIR}/rollback/available-backups.txt post_rollback_verification: depends_on: [kubernetes_rollback, docker_rollback_guide] steps: - name: generate_rollback_report image: alpine:latest commands: - | mkdir -p ${LOGS_DIR}/rollback cat > ${LOGS_DIR}/rollback/ROLLBACK_REPORT.md << 'EOF' # Rollback Execution Report **Rollback Time**: $(date -u +'%Y-%m-%dT%H:%M:%SZ') **Target**: ${Rollback_Target:-kubernetes} **Environment**: ${Rollback_Environment:-staging} **Deployment**: ${Rollback_Deployment:-all} **Revision**: ${Rollback_Revision:-0 (previous)} **Pipeline**: ${CI_BUILD_LINK} ## Status - **Pre-rollback Checks**: āœ… Passed - **Rollback Execution**: In Progress - **Post-rollback Verification**: Pending ## Artifacts Check the following for detailed information: - `pre-rollback-snapshot.txt` - Initial state snapshot - `pre-rollback-status.txt` - Pre-rollback deployments - `post-rollback-status.txt` - Post-rollback status - `rollback-output.log` - Rollback script output - `DOCKER_ROLLBACK_GUIDE.md` - Docker rollback instructions (if applicable) ## Next Steps 1. Verify all services are running 2. Check application logs for errors 3. Run health checks 4. Monitor metrics and alerts 5. Investigate root cause of previous deployment failure 6. Plan corrected deployment ## Rollback Verification Commands ### For Kubernetes ```bash # Check current deployments kubectl get deployments -n ${VAPORA_NAMESPACE} kubectl get pods -n ${VAPORA_NAMESPACE} # View logs kubectl logs -f deployment/vapora-backend -n ${VAPORA_NAMESPACE} # Check rollout history kubectl rollout history deployment/vapora-backend -n ${VAPORA_NAMESPACE} # View recent events kubectl get events -n ${VAPORA_NAMESPACE} --sort-by='.lastTimestamp' ``` ### For Docker ```bash # Check container status docker compose ps # View logs docker compose logs -f # Check service health curl http://localhost:8001/health ``` EOF cat ${LOGS_DIR}/rollback/ROLLBACK_REPORT.md publish: depends_on: [post_rollback_verification] steps: - name: publish_rollback_artifacts image: alpine:latest commands: - echo "šŸ“¦ Rollback artifacts published" - echo "" - ls -lah ${LOGS_DIR}/rollback/ - echo "" - du -sh ${LOGS_DIR}/rollback/ - name: notify_slack image: alpine:latest environment: SLACK_WEBHOOK: ${SLACK_WEBHOOK_ALERTS} commands: - | if [ -n "$SLACK_WEBHOOK" ]; then apk add --no-cache curl jq curl -X POST $SLACK_WEBHOOK \ -H 'Content-Type: application/json' \ -d '{ "text": "šŸ”™ VAPORA Rollback Executed", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "šŸ”™ **VAPORA Rollback Executed**\n\n*Rollback Details:*\n• Target: ${Rollback_Target:-kubernetes}\n• Environment: ${Rollback_Environment:-staging}\n• Deployment: ${Rollback_Deployment:-all}" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "*āš ļø Action Required:*\n1. Verify service health\n2. Review application logs\n3. Investigate root cause\n4. Plan corrected deployment" } }, { "type": "context", "elements": [ { "type": "mrkdwn", "text": "*Reports*: Check rollback artifacts in logs\n*Commit*: '"${CI_COMMIT_SHA:0:8}"'\n*Branch*: '"${CI_COMMIT_BRANCH}"'" } ] } ] }' else echo "āš ļø Slack webhook not configured" fi