#!/usr/bin/env bash set -euo pipefail # Rollback Script # Sprint: CI/CD Enhancement - Deployment Safety # # Purpose: Execute rollback to a previous version # Usage: # ./rollback.sh --environment --version --services --reason # # Exit codes: # 0 - Rollback successful # 1 - General error # 2 - Invalid arguments # 3 - Deployment failed # 4 - Health check failed SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $*" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" } log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2 } log_step() { echo -e "${BLUE}[STEP]${NC} $*" } usage() { cat << EOF Usage: $(basename "$0") [OPTIONS] Execute rollback to a previous version. Options: --environment Target environment (staging|production) --version Target version to rollback to --services JSON array of services to rollback --reason Reason for rollback --dry-run Show what would be done without executing --help, -h Show this help message Examples: $(basename "$0") --environment staging --version 1.2.3 --services '["scanner"]' --reason "Bug fix" $(basename "$0") --environment production --version 1.2.0 --services '["authority","scanner"]' --reason "Hotfix rollback" Exit codes: 0 Rollback successful 1 General error 2 Invalid arguments 3 Deployment failed 4 Health check failed EOF } # Default values ENVIRONMENT="" VERSION="" SERVICES="" REASON="" DRY_RUN=false # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in --environment) ENVIRONMENT="$2" shift 2 ;; --version) VERSION="$2" shift 2 ;; --services) SERVICES="$2" shift 2 ;; --reason) REASON="$2" shift 2 ;; --dry-run) DRY_RUN=true shift ;; --help|-h) usage exit 0 ;; *) log_error "Unknown option: $1" usage exit 2 ;; esac done # Validate required arguments if [[ -z "$ENVIRONMENT" ]] || [[ -z "$VERSION" ]] || [[ -z "$SERVICES" ]]; then log_error "Missing required arguments" usage exit 2 fi # Validate environment if [[ "$ENVIRONMENT" != "staging" ]] && [[ "$ENVIRONMENT" != "production" ]]; then log_error "Invalid environment: $ENVIRONMENT (must be staging or production)" exit 2 fi # Validate services JSON if ! echo "$SERVICES" | jq empty 2>/dev/null; then log_error "Invalid services JSON: $SERVICES" exit 2 fi log_info "Starting rollback process" log_info " Environment: $ENVIRONMENT" log_info " Version: $VERSION" log_info " Services: $SERVICES" log_info " Reason: $REASON" log_info " Dry run: $DRY_RUN" # Record start time START_TIME=$(date +%s) # Rollback each service FAILED_SERVICES=() SUCCESSFUL_SERVICES=() echo "$SERVICES" | jq -r '.[]' | while read -r service; do log_step "Rolling back $service to $VERSION..." if [[ "$DRY_RUN" == "true" ]]; then log_info " [DRY RUN] Would rollback $service" continue fi # Determine deployment method HELM_RELEASE="stellaops-${service}" NAMESPACE="stellaops-${ENVIRONMENT}" # Check if Helm release exists if helm status "$HELM_RELEASE" -n "$NAMESPACE" >/dev/null 2>&1; then log_info " Using Helm rollback for $service" # Get revision for target version REVISION=$(helm history "$HELM_RELEASE" -n "$NAMESPACE" --output json | \ jq -r --arg ver "$VERSION" '.[] | select(.app_version == $ver) | .revision' | tail -1) if [[ -n "$REVISION" ]]; then if helm rollback "$HELM_RELEASE" "$REVISION" -n "$NAMESPACE" --wait --timeout 5m; then log_info " Successfully rolled back $service to revision $REVISION" SUCCESSFUL_SERVICES+=("$service") else log_error " Failed to rollback $service" FAILED_SERVICES+=("$service") fi else log_warn " No Helm revision found for version $VERSION" log_info " Attempting deployment with specific version..." # Try to deploy specific version IMAGE_TAG="${VERSION}" VALUES_FILE="${REPO_ROOT}/devops/helm/values-${ENVIRONMENT}.yaml" if helm upgrade "$HELM_RELEASE" "${REPO_ROOT}/devops/helm/stellaops" \ -n "$NAMESPACE" \ --set "services.${service}.image.tag=${IMAGE_TAG}" \ -f "$VALUES_FILE" \ --wait --timeout 5m 2>/dev/null; then log_info " Deployed $service with version $VERSION" SUCCESSFUL_SERVICES+=("$service") else log_error " Failed to deploy $service with version $VERSION" FAILED_SERVICES+=("$service") fi fi else log_warn " No Helm release found for $service" log_info " Attempting kubectl rollout undo..." DEPLOYMENT="stellaops-${service}" if kubectl rollout undo deployment/"$DEPLOYMENT" -n "$NAMESPACE" 2>/dev/null; then log_info " Rolled back deployment $DEPLOYMENT" SUCCESSFUL_SERVICES+=("$service") else log_error " Failed to rollback deployment $DEPLOYMENT" FAILED_SERVICES+=("$service") fi fi done # Calculate duration END_TIME=$(date +%s) DURATION=$((END_TIME - START_TIME)) # Summary echo "" log_info "Rollback completed in ${DURATION}s" log_info " Successful: ${#SUCCESSFUL_SERVICES[@]}" log_info " Failed: ${#FAILED_SERVICES[@]}" if [[ ${#FAILED_SERVICES[@]} -gt 0 ]]; then log_error "Failed services: ${FAILED_SERVICES[*]}" exit 3 fi log_info "Rollback successful" exit 0