458 lines
14 KiB
Bash
Executable File
458 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Calejo Control Adapter - Remote SSH Deployment Script
|
|
# Deploys the application to remote servers over SSH
|
|
|
|
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 configuration
|
|
CONFIG_FILE=""
|
|
ENVIRONMENT=""
|
|
DRY_RUN=false
|
|
VERBOSE=false
|
|
|
|
# Function to print colored output
|
|
print_status() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Function to display usage
|
|
usage() {
|
|
echo "Usage: $0 -e <environment> [-c <config-file>] [--dry-run] [--verbose]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -e, --environment Deployment environment (production, staging)"
|
|
echo " -c, --config Custom configuration file"
|
|
echo " --dry-run Show what would be deployed without actually deploying"
|
|
echo " --verbose Enable verbose output"
|
|
echo " -h, --help Show this help message"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 -e staging # Deploy to staging"
|
|
echo " $0 -e production --dry-run # Dry run for production"
|
|
echo " $0 -e production -c custom.yml # Use custom config"
|
|
}
|
|
|
|
# Function to parse command line arguments
|
|
parse_arguments() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-e|--environment)
|
|
ENVIRONMENT="$2"
|
|
shift 2
|
|
;;
|
|
-c|--config)
|
|
CONFIG_FILE="$2"
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
print_error "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Validate required arguments
|
|
if [[ -z "$ENVIRONMENT" ]]; then
|
|
print_error "Environment is required"
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
# Set default config file if not provided
|
|
if [[ -z "$CONFIG_FILE" ]]; then
|
|
CONFIG_FILE="deploy/config/${ENVIRONMENT}.yml"
|
|
fi
|
|
|
|
# Validate config file exists
|
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
print_error "Configuration file not found: $CONFIG_FILE"
|
|
echo "Available configurations:"
|
|
ls -1 deploy/config/*.yml 2>/dev/null | sed 's|deploy/config/||' || echo " (none)"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Function to load configuration
|
|
load_configuration() {
|
|
print_status "Loading configuration from: $CONFIG_FILE"
|
|
|
|
# Check if yq is available for YAML parsing
|
|
if ! command -v yq &> /dev/null; then
|
|
print_error "yq is required for YAML parsing. Install with: apt-get install yq"
|
|
exit 1
|
|
fi
|
|
|
|
# Extract configuration values (yq with jq syntax)
|
|
SSH_HOST=$(yq -r '.ssh.host' "$CONFIG_FILE")
|
|
SSH_PORT=$(yq -r '.ssh.port' "$CONFIG_FILE")
|
|
SSH_USERNAME=$(yq -r '.ssh.username' "$CONFIG_FILE")
|
|
SSH_KEY_FILE=$(yq -r '.ssh.key_file' "$CONFIG_FILE")
|
|
|
|
TARGET_DIR=$(yq -r '.deployment.target_dir' "$CONFIG_FILE")
|
|
BACKUP_DIR=$(yq -r '.deployment.backup_dir' "$CONFIG_FILE")
|
|
LOG_DIR=$(yq -r '.deployment.log_dir' "$CONFIG_FILE")
|
|
CONFIG_DIR=$(yq -r '.deployment.config_dir' "$CONFIG_FILE")
|
|
|
|
# Validate required configuration
|
|
if [[ -z "$SSH_HOST" || -z "$SSH_USERNAME" || -z "$SSH_KEY_FILE" ]]; then
|
|
print_error "Missing required SSH configuration in $CONFIG_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate SSH key file exists
|
|
if [[ ! -f "$SSH_KEY_FILE" ]]; then
|
|
print_error "SSH key file not found: $SSH_KEY_FILE"
|
|
echo "Available keys:"
|
|
ls -1 deploy/keys/ 2>/dev/null || echo " (none)"
|
|
exit 1
|
|
fi
|
|
|
|
# Set default port if not specified
|
|
if [[ -z "$SSH_PORT" ]]; then
|
|
SSH_PORT=22
|
|
fi
|
|
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
print_status "Configuration loaded:"
|
|
echo " SSH Host: $SSH_HOST"
|
|
echo " SSH Port: $SSH_PORT"
|
|
echo " SSH Username: $SSH_USERNAME"
|
|
echo " SSH Key: $SSH_KEY_FILE"
|
|
echo " Target Directory: $TARGET_DIR"
|
|
fi
|
|
}
|
|
|
|
# Function to build SSH command
|
|
build_ssh_command() {
|
|
local cmd="$1"
|
|
local ssh_opts="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30"
|
|
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
ssh_opts="$ssh_opts -v"
|
|
fi
|
|
|
|
echo "ssh -i $SSH_KEY_FILE -p $SSH_PORT $ssh_opts $SSH_USERNAME@$SSH_HOST '$cmd'"
|
|
}
|
|
|
|
# Function to execute remote command
|
|
execute_remote() {
|
|
local cmd="$1"
|
|
local description="$2"
|
|
|
|
print_status "$description"
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
echo " [DRY RUN] Would execute: $cmd"
|
|
return 0
|
|
fi
|
|
|
|
local ssh_cmd=$(build_ssh_command "$cmd")
|
|
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
echo " Executing: $ssh_cmd"
|
|
fi
|
|
|
|
if eval "$ssh_cmd"; then
|
|
print_success "$description completed"
|
|
return 0
|
|
else
|
|
print_error "$description failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to transfer files
|
|
transfer_files() {
|
|
local local_path="$1"
|
|
local remote_path="$2"
|
|
local description="$3"
|
|
|
|
print_status "$description"
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
echo " [DRY RUN] Would transfer: $local_path -> $SSH_USERNAME@$SSH_HOST:$remote_path"
|
|
return 0
|
|
fi
|
|
|
|
local scp_cmd="scp -i $SSH_KEY_FILE -P $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -r $local_path $SSH_USERNAME@$SSH_HOST:$remote_path"
|
|
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
echo " Executing: $scp_cmd"
|
|
fi
|
|
|
|
if eval "$scp_cmd"; then
|
|
print_success "$description completed"
|
|
return 0
|
|
else
|
|
print_error "$description failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to check remote prerequisites
|
|
check_remote_prerequisites() {
|
|
print_status "Checking remote prerequisites..."
|
|
|
|
# Check Docker
|
|
execute_remote "command -v docker" "Checking Docker installation" || {
|
|
print_error "Docker is not installed on remote server"
|
|
return 1
|
|
}
|
|
|
|
# Check Docker Compose
|
|
execute_remote "command -v docker-compose" "Checking Docker Compose installation" || {
|
|
print_error "Docker Compose is not installed on remote server"
|
|
return 1
|
|
}
|
|
|
|
# Check disk space
|
|
execute_remote "df -h /" "Checking disk space"
|
|
|
|
print_success "Remote prerequisites check passed"
|
|
}
|
|
|
|
# Function to create remote directories
|
|
create_remote_directories() {
|
|
print_status "Creating remote directories..."
|
|
|
|
local dirs=("$TARGET_DIR" "$BACKUP_DIR" "$LOG_DIR" "$CONFIG_DIR")
|
|
|
|
for dir in "${dirs[@]}"; do
|
|
execute_remote "sudo mkdir -p $dir && sudo chown $SSH_USERNAME:$SSH_USERNAME $dir" "Creating directory: $dir"
|
|
done
|
|
|
|
print_success "Remote directories created"
|
|
}
|
|
|
|
# Function to backup existing deployment
|
|
backup_existing_deployment() {
|
|
print_status "Checking for existing deployment..."
|
|
|
|
# Check if target directory exists and has content
|
|
if execute_remote "[ -d $TARGET_DIR ] && [ \"$(ls -A $TARGET_DIR)\" ]" "Checking existing deployment" 2>/dev/null; then
|
|
print_warning "Existing deployment found, creating backup..."
|
|
|
|
local timestamp=$(date +%Y%m%d_%H%M%S)
|
|
local backup_file="calejo-backup-$timestamp.tar.gz"
|
|
|
|
execute_remote "cd $TARGET_DIR && tar -czf $BACKUP_DIR/$backup_file ." "Creating backup: $backup_file"
|
|
|
|
print_success "Backup created: $BACKUP_DIR/$backup_file"
|
|
else
|
|
print_status "No existing deployment found"
|
|
fi
|
|
}
|
|
|
|
# Function to transfer application files
|
|
transfer_application() {
|
|
print_status "Transferring application files..."
|
|
|
|
# Create temporary deployment package
|
|
local temp_dir=$(mktemp -d)
|
|
local package_name="calejo-deployment-$(date +%Y%m%d_%H%M%S).tar.gz"
|
|
|
|
# Copy files to temporary directory (excluding deployment config and keys)
|
|
print_status "Creating deployment package..."
|
|
cp -r . "$temp_dir/"
|
|
|
|
# Remove sensitive deployment files from package
|
|
rm -rf "$temp_dir/deploy/config"
|
|
rm -rf "$temp_dir/deploy/keys"
|
|
|
|
# Create package
|
|
cd "$temp_dir" && tar -czf "/tmp/$package_name" . && cd - > /dev/null
|
|
|
|
# Transfer package
|
|
transfer_files "/tmp/$package_name" "$TARGET_DIR/" "Transferring deployment package"
|
|
|
|
# Extract package on remote
|
|
execute_remote "cd $TARGET_DIR && tar -xzf $package_name && rm $package_name" "Extracting deployment package"
|
|
|
|
# Clean up
|
|
rm -rf "$temp_dir"
|
|
rm -f "/tmp/$package_name"
|
|
|
|
print_success "Application files transferred"
|
|
}
|
|
|
|
# Function to setup remote configuration
|
|
setup_remote_configuration() {
|
|
print_status "Setting up remote configuration..."
|
|
|
|
# Transfer configuration files
|
|
if [[ -f "config/settings.py" ]]; then
|
|
transfer_files "config/settings.py" "$CONFIG_DIR/" "Transferring configuration file"
|
|
fi
|
|
|
|
# Set permissions on scripts
|
|
execute_remote "chmod +x $TARGET_DIR/scripts/*.sh" "Setting script permissions"
|
|
execute_remote "chmod +x $TARGET_DIR/deploy-onprem.sh" "Setting deployment script permissions"
|
|
|
|
print_success "Remote configuration setup completed"
|
|
}
|
|
|
|
# Function to build and start services
|
|
build_and_start_services() {
|
|
print_status "Building and starting services..."
|
|
|
|
# Build services
|
|
execute_remote "cd $TARGET_DIR && sudo docker-compose build" "Building Docker images"
|
|
|
|
# Start services - use production compose file if available
|
|
if [[ "$ENVIRONMENT" == "production" ]] && execute_remote "cd $TARGET_DIR && test -f docker-compose.production.yml" "Checking for production compose file" 2>/dev/null; then
|
|
execute_remote "cd $TARGET_DIR && sudo docker-compose -f docker-compose.production.yml up -d" "Starting services with production configuration"
|
|
else
|
|
execute_remote "cd $TARGET_DIR && sudo docker-compose up -d" "Starting services"
|
|
fi
|
|
|
|
# Wait for services to be ready
|
|
print_status "Waiting for services to start..."
|
|
for i in {1..30}; do
|
|
if execute_remote "curl -s http://localhost:8080/health > /dev/null" "Checking service health" 2>/dev/null; then
|
|
print_success "Services started successfully"
|
|
break
|
|
fi
|
|
echo " Waiting... (attempt $i/30)"
|
|
sleep 2
|
|
|
|
if [[ $i -eq 30 ]]; then
|
|
print_error "Services failed to start within 60 seconds"
|
|
execute_remote "cd $TARGET_DIR && sudo docker-compose logs" "Checking service logs"
|
|
return 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Function to validate deployment
|
|
validate_deployment() {
|
|
print_status "Validating deployment..."
|
|
|
|
# Run remote validation script
|
|
if execute_remote "cd $TARGET_DIR && ./validate-deployment.sh" "Running deployment validation" 2>/dev/null; then
|
|
print_success "Deployment validation passed"
|
|
else
|
|
print_warning "Deployment validation completed with warnings"
|
|
fi
|
|
|
|
# Test key endpoints
|
|
local endpoints=("/health" "/dashboard" "/api/v1/status")
|
|
|
|
for endpoint in "${endpoints[@]}"; do
|
|
if execute_remote "curl -s -f http://localhost:8080$endpoint > /dev/null" "Testing endpoint: $endpoint" 2>/dev/null; then
|
|
print_success "Endpoint $endpoint is accessible"
|
|
else
|
|
print_error "Endpoint $endpoint is not accessible"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Function to display deployment summary
|
|
display_deployment_summary() {
|
|
print_success "Deployment to $ENVIRONMENT completed successfully!"
|
|
echo ""
|
|
echo "=================================================="
|
|
echo " DEPLOYMENT SUMMARY"
|
|
echo "=================================================="
|
|
echo ""
|
|
echo "🌍 Environment: $ENVIRONMENT"
|
|
echo "🏠 Server: $SSH_HOST"
|
|
echo "📁 Application: $TARGET_DIR"
|
|
echo ""
|
|
echo "🔗 Access URLs:"
|
|
echo " Dashboard: http://$SSH_HOST:8080/dashboard"
|
|
echo " REST API: http://$SSH_HOST:8080"
|
|
echo " Health Check: http://$SSH_HOST:8080/health"
|
|
echo ""
|
|
echo "🔧 Management Commands:"
|
|
echo " View logs: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$SSH_HOST 'cd $TARGET_DIR && docker-compose logs -f'"
|
|
echo " Health check: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$SSH_HOST 'cd $TARGET_DIR && ./scripts/health-check.sh'"
|
|
echo " Backup: ssh -i $SSH_KEY_FILE $SSH_USERNAME@$SSH_HOST 'cd $TARGET_DIR && ./scripts/backup-full.sh'"
|
|
echo ""
|
|
echo "=================================================="
|
|
}
|
|
|
|
# Main deployment function
|
|
main() {
|
|
echo ""
|
|
echo "🚀 Calejo Control Adapter - Remote SSH Deployment"
|
|
echo "=================================================="
|
|
echo ""
|
|
|
|
# Parse command line arguments
|
|
parse_arguments "$@"
|
|
|
|
# Load configuration
|
|
load_configuration
|
|
|
|
# Display deployment info
|
|
echo "Deploying to: $ENVIRONMENT"
|
|
echo "Server: $SSH_HOST"
|
|
echo "Config: $CONFIG_FILE"
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
echo "Mode: DRY RUN (no changes will be made)"
|
|
fi
|
|
echo ""
|
|
|
|
# Check remote prerequisites
|
|
check_remote_prerequisites
|
|
|
|
# Create remote directories
|
|
create_remote_directories
|
|
|
|
# Backup existing deployment
|
|
backup_existing_deployment
|
|
|
|
# Transfer application files
|
|
transfer_application
|
|
|
|
# Setup remote configuration
|
|
setup_remote_configuration
|
|
|
|
# Build and start services
|
|
build_and_start_services
|
|
|
|
# Validate deployment
|
|
validate_deployment
|
|
|
|
# Display summary
|
|
display_deployment_summary
|
|
|
|
echo ""
|
|
print_success "Remote deployment completed!"
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |