
Some checks failed
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Build Docker Image (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Performance Benchmarks (push) Has been cancelled
CI/CD Pipeline / Cleanup (push) Has been cancelled
564 lines
15 KiB
Bash
Executable File
564 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Rustelo Application Deployment Script
|
|
# This script handles deployment of the Rustelo application in various environments
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Default values
|
|
ENVIRONMENT="production"
|
|
COMPOSE_FILE="docker-compose.yml"
|
|
BUILD_ARGS=""
|
|
MIGRATE_DB=false
|
|
BACKUP_DB=false
|
|
HEALTH_CHECK=true
|
|
TIMEOUT=300
|
|
PROJECT_NAME="rustelo"
|
|
DOCKER_REGISTRY=""
|
|
IMAGE_TAG="latest"
|
|
FORCE_RECREATE=false
|
|
SCALE_REPLICAS=1
|
|
FEATURES="production"
|
|
USE_DEFAULT_FEATURES=false
|
|
|
|
# Function to print colored output
|
|
print_status() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
print_debug() {
|
|
if [[ "$DEBUG" == "true" ]]; then
|
|
echo -e "${BLUE}[DEBUG]${NC} $1"
|
|
fi
|
|
}
|
|
|
|
# Function to show usage
|
|
show_usage() {
|
|
cat << EOF
|
|
Usage: $0 [OPTIONS] COMMAND
|
|
|
|
Commands:
|
|
deploy Deploy the application
|
|
stop Stop the application
|
|
restart Restart the application
|
|
status Show deployment status
|
|
logs Show application logs
|
|
scale Scale application replicas
|
|
backup Create database backup
|
|
migrate Run database migrations
|
|
rollback Rollback to previous version
|
|
health Check application health
|
|
update Update application to latest version
|
|
clean Clean up unused containers and images
|
|
|
|
Options:
|
|
-e, --env ENV Environment (dev|staging|production) [default: production]
|
|
-f, --file FILE Docker compose file [default: docker-compose.yml]
|
|
-p, --project PROJECT Project name [default: rustelo]
|
|
-t, --tag TAG Docker image tag [default: latest]
|
|
-r, --registry REGISTRY Docker registry URL
|
|
-s, --scale REPLICAS Number of replicas [default: 1]
|
|
--migrate Run database migrations before deployment
|
|
--backup Create database backup before deployment
|
|
--no-health-check Skip health check after deployment
|
|
--force-recreate Force recreation of containers
|
|
--timeout SECONDS Deployment timeout [default: 300]
|
|
--build-arg ARG Docker build arguments
|
|
--features FEATURES Cargo features to enable [default: production]
|
|
--default-features Use default features instead of custom
|
|
--debug Enable debug output
|
|
-h, --help Show this help message
|
|
|
|
Examples:
|
|
$0 deploy # Deploy production
|
|
$0 deploy -e staging # Deploy staging
|
|
$0 deploy --migrate --backup # Deploy with migration and backup
|
|
$0 scale -s 3 # Scale to 3 replicas
|
|
$0 logs -f # Follow logs
|
|
$0 health # Check health status
|
|
$0 deploy --features "auth,metrics" # Deploy with specific features
|
|
$0 deploy --default-features # Deploy with all default features
|
|
|
|
Environment Variables:
|
|
DOCKER_REGISTRY Docker registry URL
|
|
RUSTELO_ENV Environment override
|
|
COMPOSE_PROJECT_NAME Docker compose project name
|
|
DATABASE_URL Database connection string
|
|
DEBUG Enable debug mode
|
|
EOF
|
|
}
|
|
|
|
# Function to parse command line arguments
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-e|--env)
|
|
ENVIRONMENT="$2"
|
|
shift 2
|
|
;;
|
|
-f|--file)
|
|
COMPOSE_FILE="$2"
|
|
shift 2
|
|
;;
|
|
-p|--project)
|
|
PROJECT_NAME="$2"
|
|
shift 2
|
|
;;
|
|
-t|--tag)
|
|
IMAGE_TAG="$2"
|
|
shift 2
|
|
;;
|
|
-r|--registry)
|
|
DOCKER_REGISTRY="$2"
|
|
shift 2
|
|
;;
|
|
-s|--scale)
|
|
SCALE_REPLICAS="$2"
|
|
shift 2
|
|
;;
|
|
--migrate)
|
|
MIGRATE_DB=true
|
|
shift
|
|
;;
|
|
--backup)
|
|
BACKUP_DB=true
|
|
shift
|
|
;;
|
|
--no-health-check)
|
|
HEALTH_CHECK=false
|
|
shift
|
|
;;
|
|
--force-recreate)
|
|
FORCE_RECREATE=true
|
|
shift
|
|
;;
|
|
--timeout)
|
|
TIMEOUT="$2"
|
|
shift 2
|
|
;;
|
|
--build-arg)
|
|
BUILD_ARGS="$BUILD_ARGS --build-arg $2"
|
|
shift 2
|
|
;;
|
|
--features)
|
|
FEATURES="$2"
|
|
shift 2
|
|
;;
|
|
--default-features)
|
|
USE_DEFAULT_FEATURES=true
|
|
shift
|
|
;;
|
|
--debug)
|
|
DEBUG=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
print_error "Unknown option: $1"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
COMMAND="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Function to validate environment
|
|
validate_environment() {
|
|
case $ENVIRONMENT in
|
|
dev|development)
|
|
ENVIRONMENT="development"
|
|
COMPOSE_FILE="docker-compose.yml"
|
|
;;
|
|
staging)
|
|
ENVIRONMENT="staging"
|
|
COMPOSE_FILE="docker-compose.staging.yml"
|
|
;;
|
|
prod|production)
|
|
ENVIRONMENT="production"
|
|
COMPOSE_FILE="docker-compose.yml"
|
|
;;
|
|
*)
|
|
print_error "Invalid environment: $ENVIRONMENT"
|
|
print_error "Valid environments: dev, staging, production"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Function to check prerequisites
|
|
check_prerequisites() {
|
|
print_status "Checking prerequisites..."
|
|
|
|
# Check if Docker is installed and running
|
|
if ! command -v docker &> /dev/null; then
|
|
print_error "Docker is not installed or not in PATH"
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker info &> /dev/null; then
|
|
print_error "Docker daemon is not running"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if Docker Compose is installed
|
|
if ! command -v docker-compose &> /dev/null; then
|
|
print_error "Docker Compose is not installed or not in PATH"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if compose file exists
|
|
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
|
print_error "Compose file not found: $COMPOSE_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
print_status "Prerequisites check passed"
|
|
}
|
|
|
|
# Function to set environment variables
|
|
set_environment_vars() {
|
|
export COMPOSE_PROJECT_NAME="${PROJECT_NAME}"
|
|
export DOCKER_REGISTRY="${DOCKER_REGISTRY}"
|
|
export IMAGE_TAG="${IMAGE_TAG}"
|
|
export ENVIRONMENT="${ENVIRONMENT}"
|
|
|
|
# Source environment-specific variables
|
|
if [[ -f ".env.${ENVIRONMENT}" ]]; then
|
|
print_status "Loading environment variables from .env.${ENVIRONMENT}"
|
|
source ".env.${ENVIRONMENT}"
|
|
elif [[ -f ".env" ]]; then
|
|
print_status "Loading environment variables from .env"
|
|
source ".env"
|
|
fi
|
|
|
|
print_debug "Environment variables set:"
|
|
print_debug " COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}"
|
|
print_debug " DOCKER_REGISTRY=${DOCKER_REGISTRY}"
|
|
print_debug " IMAGE_TAG=${IMAGE_TAG}"
|
|
print_debug " ENVIRONMENT=${ENVIRONMENT}"
|
|
print_debug " FEATURES=${FEATURES}"
|
|
print_debug " USE_DEFAULT_FEATURES=${USE_DEFAULT_FEATURES}"
|
|
}
|
|
|
|
# Function to build Docker images
|
|
build_images() {
|
|
print_status "Building Docker images..."
|
|
|
|
local build_cmd="docker-compose -f $COMPOSE_FILE build"
|
|
|
|
if [[ -n "$BUILD_ARGS" ]]; then
|
|
build_cmd="$build_cmd $BUILD_ARGS"
|
|
fi
|
|
|
|
# Add feature arguments to build args
|
|
if [[ "$USE_DEFAULT_FEATURES" == "false" ]]; then
|
|
build_cmd="$build_cmd --build-arg CARGO_FEATURES=\"$FEATURES\" --build-arg NO_DEFAULT_FEATURES=\"true\""
|
|
else
|
|
build_cmd="$build_cmd --build-arg CARGO_FEATURES=\"\" --build-arg NO_DEFAULT_FEATURES=\"false\""
|
|
fi
|
|
|
|
if [[ "$DEBUG" == "true" ]]; then
|
|
print_debug "Build command: $build_cmd"
|
|
fi
|
|
|
|
if ! $build_cmd; then
|
|
print_error "Failed to build Docker images"
|
|
exit 1
|
|
fi
|
|
|
|
print_status "Docker images built successfully"
|
|
}
|
|
|
|
# Function to create database backup
|
|
create_backup() {
|
|
if [[ "$BACKUP_DB" == "true" ]]; then
|
|
print_status "Creating database backup..."
|
|
|
|
local backup_file="backup_$(date +%Y%m%d_%H%M%S).sql"
|
|
|
|
if docker-compose -f "$COMPOSE_FILE" exec -T db pg_dump -U postgres rustelo_prod > "$backup_file"; then
|
|
print_status "Database backup created: $backup_file"
|
|
else
|
|
print_error "Failed to create database backup"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to run database migrations
|
|
run_migrations() {
|
|
if [[ "$MIGRATE_DB" == "true" ]]; then
|
|
print_status "Running database migrations..."
|
|
|
|
if docker-compose -f "$COMPOSE_FILE" run --rm migrate; then
|
|
print_status "Database migrations completed successfully"
|
|
else
|
|
print_error "Database migrations failed"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to deploy application
|
|
deploy_application() {
|
|
print_status "Deploying application..."
|
|
|
|
local compose_cmd="docker-compose -f $COMPOSE_FILE up -d"
|
|
|
|
if [[ "$FORCE_RECREATE" == "true" ]]; then
|
|
compose_cmd="$compose_cmd --force-recreate"
|
|
fi
|
|
|
|
if [[ "$SCALE_REPLICAS" -gt 1 ]]; then
|
|
compose_cmd="$compose_cmd --scale app=$SCALE_REPLICAS"
|
|
fi
|
|
|
|
if [[ "$DEBUG" == "true" ]]; then
|
|
print_debug "Deploy command: $compose_cmd"
|
|
fi
|
|
|
|
if ! $compose_cmd; then
|
|
print_error "Failed to deploy application"
|
|
exit 1
|
|
fi
|
|
|
|
print_status "Application deployed successfully"
|
|
}
|
|
|
|
# Function to wait for application to be ready
|
|
wait_for_health() {
|
|
if [[ "$HEALTH_CHECK" == "true" ]]; then
|
|
print_status "Waiting for application to be healthy..."
|
|
|
|
local start_time=$(date +%s)
|
|
local health_url="http://localhost:3030/health"
|
|
|
|
while true; do
|
|
local current_time=$(date +%s)
|
|
local elapsed=$((current_time - start_time))
|
|
|
|
if [[ $elapsed -gt $TIMEOUT ]]; then
|
|
print_error "Health check timeout after ${TIMEOUT} seconds"
|
|
exit 1
|
|
fi
|
|
|
|
if curl -f -s "$health_url" > /dev/null 2>&1; then
|
|
print_status "Application is healthy"
|
|
break
|
|
fi
|
|
|
|
print_debug "Health check failed, retrying in 5 seconds... (${elapsed}s elapsed)"
|
|
sleep 5
|
|
done
|
|
fi
|
|
}
|
|
|
|
# Function to show deployment status
|
|
show_status() {
|
|
print_status "Deployment status:"
|
|
docker-compose -f "$COMPOSE_FILE" ps
|
|
|
|
print_status "Container resource usage:"
|
|
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
|
|
}
|
|
|
|
# Function to show logs
|
|
show_logs() {
|
|
local follow_flag=""
|
|
if [[ "$1" == "-f" ]]; then
|
|
follow_flag="-f"
|
|
fi
|
|
|
|
docker-compose -f "$COMPOSE_FILE" logs $follow_flag
|
|
}
|
|
|
|
# Function to scale application
|
|
scale_application() {
|
|
print_status "Scaling application to $SCALE_REPLICAS replicas..."
|
|
|
|
if docker-compose -f "$COMPOSE_FILE" up -d --scale app="$SCALE_REPLICAS"; then
|
|
print_status "Application scaled successfully"
|
|
else
|
|
print_error "Failed to scale application"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Function to stop application
|
|
stop_application() {
|
|
print_status "Stopping application..."
|
|
|
|
if docker-compose -f "$COMPOSE_FILE" down; then
|
|
print_status "Application stopped successfully"
|
|
else
|
|
print_error "Failed to stop application"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Function to restart application
|
|
restart_application() {
|
|
print_status "Restarting application..."
|
|
|
|
if docker-compose -f "$COMPOSE_FILE" restart; then
|
|
print_status "Application restarted successfully"
|
|
else
|
|
print_error "Failed to restart application"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Function to check application health
|
|
check_health() {
|
|
print_status "Checking application health..."
|
|
|
|
local health_url="http://localhost:3030/health"
|
|
|
|
if curl -f -s "$health_url" | jq '.status' | grep -q "healthy"; then
|
|
print_status "Application is healthy"
|
|
|
|
# Show detailed health information
|
|
curl -s "$health_url" | jq .
|
|
else
|
|
print_error "Application is not healthy"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Function to update application
|
|
update_application() {
|
|
print_status "Updating application..."
|
|
|
|
# Pull latest images
|
|
docker-compose -f "$COMPOSE_FILE" pull
|
|
|
|
# Restart with new images
|
|
docker-compose -f "$COMPOSE_FILE" up -d --force-recreate
|
|
|
|
print_status "Application updated successfully"
|
|
}
|
|
|
|
# Function to rollback application
|
|
rollback_application() {
|
|
print_warning "Rollback functionality not implemented yet"
|
|
print_warning "Please manually specify the desired image tag and redeploy"
|
|
}
|
|
|
|
# Function to clean up
|
|
cleanup() {
|
|
print_status "Cleaning up unused containers and images..."
|
|
|
|
# Remove stopped containers
|
|
docker container prune -f
|
|
|
|
# Remove unused images
|
|
docker image prune -f
|
|
|
|
# Remove unused volumes
|
|
docker volume prune -f
|
|
|
|
# Remove unused networks
|
|
docker network prune -f
|
|
|
|
print_status "Cleanup completed"
|
|
}
|
|
|
|
# Main function
|
|
main() {
|
|
# Parse command line arguments
|
|
parse_args "$@"
|
|
|
|
# Validate command
|
|
if [[ -z "$COMMAND" ]]; then
|
|
print_error "No command specified"
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
|
|
# Validate environment
|
|
validate_environment
|
|
|
|
# Check prerequisites
|
|
check_prerequisites
|
|
|
|
# Set environment variables
|
|
set_environment_vars
|
|
|
|
# Execute command
|
|
case $COMMAND in
|
|
deploy)
|
|
build_images
|
|
create_backup
|
|
run_migrations
|
|
deploy_application
|
|
wait_for_health
|
|
show_status
|
|
;;
|
|
stop)
|
|
stop_application
|
|
;;
|
|
restart)
|
|
restart_application
|
|
wait_for_health
|
|
;;
|
|
status)
|
|
show_status
|
|
;;
|
|
logs)
|
|
show_logs "$@"
|
|
;;
|
|
scale)
|
|
scale_application
|
|
;;
|
|
backup)
|
|
create_backup
|
|
;;
|
|
migrate)
|
|
run_migrations
|
|
;;
|
|
rollback)
|
|
rollback_application
|
|
;;
|
|
health)
|
|
check_health
|
|
;;
|
|
update)
|
|
update_application
|
|
wait_for_health
|
|
;;
|
|
clean)
|
|
cleanup
|
|
;;
|
|
*)
|
|
print_error "Unknown command: $COMMAND"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|