227 lines
6.0 KiB
Bash
227 lines
6.0 KiB
Bash
#!/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 <env> --version <ver> --services <json> --reason <text>
|
|
#
|
|
# 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 <env> Target environment (staging|production)
|
|
--version <version> Target version to rollback to
|
|
--services <json> JSON array of services to rollback
|
|
--reason <text> 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
|