diff --git a/scripts/ez-homelab.sh b/scripts/ez-homelab.sh index 7dbc840..98499d7 100755 --- a/scripts/ez-homelab.sh +++ b/scripts/ez-homelab.sh @@ -1,9 +1,13 @@ #!/bin/bash # EZ-Homelab Setup & Deployment Script +#═══════════════════════════════════════════════════════════ +# SECTION 1: CONFIGURATION & CONSTANTS +#═══════════════════════════════════════════════════════════ + # Debug logging configuration DEBUG=${DEBUG:-false} -VERBOSE=${VERBOSE:-false} # New verbosity toggle +VERBOSE=${VERBOSE:-false} DEBUG_LOG_FILE="/tmp/ez-homelab-debug.log" # Colors for output @@ -13,6 +17,10 @@ YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color +#═══════════════════════════════════════════════════════════ +# SECTION 2: LOGGING & UTILITY FUNCTIONS +#═══════════════════════════════════════════════════════════ + # Debug logging function debug_log() { if [ "$DEBUG" = true ]; then @@ -54,6 +62,94 @@ log_error() { debug_log "[ERROR] $1" } +# Common helper: Backup existing file +common_backup() { + local file="$1" + if [ -f "$file" ]; then + sudo cp "$file" "${file}.backup.$(date +%Y%m%d_%H%M%S)" + fi +} + +# Common helper: Create directories with proper ownership +common_create_directories() { + local -a dirs=("$@") + for dir in "${dirs[@]}"; do + sudo mkdir -p "$dir" || { log_error "Failed to create $dir"; return 1; } + done + sudo chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt/stacks /opt/dockge /opt/arcane 2>/dev/null || true +} + +# Common helper: Create Docker networks +common_create_networks() { + docker network create homelab-network 2>/dev/null || true + docker network create traefik-network 2>/dev/null || true + docker network create media-network 2>/dev/null || true +} + +# Run command function (handles dry-run and test modes) +run_cmd() { + local quiet=false + if [ "$1" = "--quiet" ]; then + quiet=true + shift + fi + + local cmd="$*" + + if [ "$DRY_RUN" = true ]; then + echo "[DRY-RUN] Would execute: $cmd" + return 0 + fi + + if [ "$TEST_MODE" = true ]; then + echo "[TEST] Simulating: $cmd" + return 0 + fi + + if [ "$quiet" = true ]; then + eval "$cmd" >/dev/null 2>&1 + return $? + else + eval "$cmd" + return $? + fi +} + +# Check if Docker is installed +check_docker_installed() { + if command -v docker &> /dev/null && docker --version &> /dev/null; then + return 0 + else + return 1 + fi +} + +# Check system resources +check_system_resources() { + local available_mem=$(free -m | awk '/^Mem:/{print $7}') + local available_disk=$(df -BG / | awk 'NR==2 {print $4}' | sed 's/G//') + + if [ "$available_mem" -lt 512 ]; then + log_warning "Low memory detected: ${available_mem}MB available" + fi + + if [ "$available_disk" -lt 10 ]; then + log_warning "Low disk space: ${available_disk}GB available" + fi +} + +# Cleanup orphaned processes +cleanup_orphaned_processes() { + debug_log "Checking for orphaned processes" + # Kill any stuck Docker Compose processes + pkill -9 -f "docker-compose" 2>/dev/null || true + pkill -9 -f "docker compose" 2>/dev/null || true +} + +#═══════════════════════════════════════════════════════════ +# SECTION 3: ENVIRONMENT & CONFIGURATION MANAGEMENT +#═══════════════════════════════════════════════════════════ + # Safely load environment variables from .env file load_env_file_safely() { local env_file="$1" @@ -118,10 +214,6 @@ load_env_file_safely() { debug_log "Env file loaded successfully" } -load_env_file() { - load_env_file_safely "$REPO_DIR/.env" -} - # Generate .env.global file without comments and blank lines generate_env_global() { local source_env="$1" @@ -196,6 +288,10 @@ process_stack_env() { debug_log "Populated .env values for $stack_dir" } +#═══════════════════════════════════════════════════════════ +# SECTION 4: FILE PROCESSING & LOCALIZATION +#═══════════════════════════════════════════════════════════ + # Localize only labels and x-dockge sections in docker-compose files localize_yml_file() { local file_path="$1" @@ -370,6 +466,10 @@ localize_deployment() { fi } +#═══════════════════════════════════════════════════════════ +# SECTION 5: INFRASTRUCTURE SETUP (TLS, CA, Docker) +#═══════════════════════════════════════════════════════════ + # Function to generate shared CA for multi-server TLS generate_shared_ca() { local ca_dir="/opt/stacks/core/shared-ca" @@ -380,17 +480,24 @@ generate_shared_ca() { log_success "Shared CA generated" } +#═══════════════════════════════════════════════════════════ +# SECTION 6: SSH CONFIGURATION FOR REMOTE SERVERS +#═══════════════════════════════════════════════════════════ + # Setup SSH key authentication to core server -setup_ssh_key_to_core() { - local key_name="id_rsa_${SERVER_HOSTNAME}_to_core" - local key_path="/home/$ACTUAL_USER/.ssh/$key_name" - - log_info "Setting up SSH key authentication to core server..." +# Check existing SSH key or cleanup +ssh_cleanup_and_check_existing() { + local key_path="$1" # Ensure .ssh directory exists mkdir -p "/home/$ACTUAL_USER/.ssh" chmod 700 "/home/$ACTUAL_USER/.ssh" + # Clean up any conflicting known_hosts entries for core server + log_info "Cleaning up known_hosts entries..." + ssh-keygen -R "${CORE_SERVER_IP}" >/dev/null 2>&1 || true + ssh-keygen -R "${CORE_SERVER_HOSTNAME}" >/dev/null 2>&1 || true + # Check if key already exists if [ -f "$key_path" ]; then log_info "SSH key already exists: $key_path" @@ -401,7 +508,6 @@ setup_ssh_key_to_core() { -o ServerAliveInterval=1 -o ServerAliveCountMax=1 -o LogLevel=ERROR \ '${CORE_SERVER_USER}@${CORE_SERVER_IP}' 'echo test' 2>&1 | grep -v 'locale\|LC_ALL\|setlocale' | grep -q 'test'"; then log_success "Existing SSH key works, skipping key setup" - export SSH_KEY_PATH="$key_path" return 0 else log_warning "Existing key doesn't work or connection failed, will regenerate and install" @@ -409,7 +515,13 @@ setup_ssh_key_to_core() { fi fi - # Generate new SSH key + return 1 +} + +# Generate new SSH key pair +ssh_generate_key() { + local key_path="$1" + log_info "Generating SSH key: $key_path" ssh-keygen -t rsa -b 4096 -f "$key_path" -N "" \ -C "${SERVER_HOSTNAME}-to-core-${CORE_SERVER_HOSTNAME}" 2>&1 | grep -v "^Generating\|^Your identification\|^Your public key" @@ -420,16 +532,22 @@ setup_ssh_key_to_core() { fi log_success "SSH key generated" + return 0 +} + +# Install SSH key on core server using password authentication +ssh_install_key_with_password() { + local key_path="$1" - # Install key on core server using password log_info "Installing SSH key on core server ${CORE_SERVER_IP}..." + # Ensure sshpass is installed if ! command -v sshpass &> /dev/null; then log_info "sshpass is not installed. Installing now..." sudo apt-get update -qq && sudo apt-get install -y sshpass >/dev/null 2>&1 fi - # Debug: Check if password is set + # Validate password is set if [ -z "$CORE_SERVER_PASSWORD" ]; then log_error "CORE_SERVER_PASSWORD is empty!" log_error "Check your .env file - ensure CORE_SERVER_PASSWORD is set correctly" @@ -476,23 +594,34 @@ setup_ssh_key_to_core() { return 1 fi + return 0 +} + +# Verify SSH key works and add config entry +ssh_verify_and_add_config() { + local key_path="$1" + # Verify key works log_info "Verifying SSH key authentication..." - if LC_ALL=C ssh -i "$key_path" -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o LogLevel=ERROR \ + if ! LC_ALL=C ssh -i "$key_path" -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o LogLevel=ERROR \ "${CORE_SERVER_USER}@${CORE_SERVER_IP}" "echo 'SSH key authentication successful'" 2>&1 | grep -v "locale\|LC_ALL\|setlocale" | grep -q "successful"; then - log_success "SSH key authentication verified" - - # Add SSH config entry for automatic key usage - log_info "Adding SSH config entry for core server..." - local ssh_config="/home/$ACTUAL_USER/.ssh/config" - - # Create config file if it doesn't exist - touch "$ssh_config" - chmod 600 "$ssh_config" - - # Check if entry already exists - if ! grep -q "Host ${CORE_SERVER_HOSTNAME}" "$ssh_config" 2>/dev/null; then - cat >> "$ssh_config" </dev/null; then + cat >> "$ssh_config" </dev/null || true - docker network create traefik-network 2>/dev/null || true + # Step 2: Create required directories + echo "║" + echo "║ Creating required directories..." + common_create_directories "/opt/stacks" "/opt/dockge" "/opt/arcane" >/dev/null 2>&1 + echo "║ /opt/stacks created" + echo "║ /opt/dockge created" + echo "║ /opt/arcane created" - # Step 3: Install envsubst if not present + # Step 3: Create required Docker networks + echo "║" + echo "║ Creating Docker networks..." + common_create_networks "homelab-network" "traefik-network" >/dev/null 2>&1 + echo "║ homelab-network created" + echo "║ traefik-network created" + + # Step 4: Install envsubst if not present if ! command -v envsubst &> /dev/null; then - log_info "Installing envsubst (gettext-base)..." + echo "║" + echo -n "║ Installing envsubst... " sudo apt-get update -qq && sudo apt-get install -y gettext-base >/dev/null 2>&1 - log_success "envsubst installed" + echo "Success" fi - # Step 4: Copy all stacks to remote server - log_info "Step 4: Copying all stacks to remote server..." - copy_all_stacks_for_remote - echo "" + # Step 5: Copy all stacks to remote server + echo "║" + echo -n "║ Copying stacks to remote server... " + copy_all_stacks_for_remote >/dev/null 2>&1 + echo "Success" - # Step 5: Configure services for additional server (remove Traefik labels) - log_info "Step 5: Configuring services for additional server..." - configure_remote_server_routing - echo "" + # Step 6: Configure services for additional server (remove Traefik labels) + echo "║" + echo -n "║ Configuring services... " + configure_remote_server_routing >/dev/null 2>&1 + echo "Success" - # Step 6: Deploy Dockge - log_info "Step 6: Deploying Dockge..." + # Step 7: Deploy Dockge + echo "║" deploy_dockge - echo "" - # Step 7: Deploy Sablier stack for local lazy loading - log_info "Step 7: Deploying Sablier stack..." + # Step 8: Deploy Arcane + deploy_arcane + + # Step 9: Deploy Sablier stack for local lazy loading + echo -n "║ Deploying Sablier... " deploy_sablier_stack - echo "" - # Step 8: Deploy Infrastructure stack - log_info "Step 8: Deploying Infrastructure stack..." + # Step 10: Deploy Infrastructure stack deploy_infrastructure - echo "" - # Step 9: Register this remote server with core Traefik - log_info "Step 9: Registering with core Traefik..." + # Step 11: Register this remote server with core Traefik + echo "║" + echo -n "║ Registering with core Traefik... " register_remote_server_with_core - echo "" log_success "Remote server deployment complete!" - echo "" - echo "This server is now configured to:" - echo " - Run Dockge for local stack management" - echo " - Run Sablier for local container lazy loading" - echo " - Run infrastructure services with exposed ports" - echo " - Be accessible via core Traefik routes" - echo "" - echo "Services deployed on this server are accessible at:" - echo " - Via core Traefik: https://servicename.${SERVER_HOSTNAME}.${DOMAIN}" - echo " - Via direct IP: http://${SERVER_IP}:PORT" - echo "" - echo "Additional stacks available in /opt/stacks/ (not started):" - echo " - dashboards, media, media-management, monitoring, productivity" - echo " - transcoders, utilities, vpn, wikis, homeassistant, alternatives" - echo "" } # Register remote server with core Traefik @@ -2021,19 +2170,19 @@ EOF local ssh_exit_code=$? - # Show output for debugging - echo "$ssh_output" | grep -v "locale\|LC_ALL\|setlocale" - if [ $ssh_exit_code -eq 0 ] && echo "$ssh_output" | grep -q "SUCCESS: Registration complete"; then - log_success "Successfully registered with core server" - log_info "Files created on core server:" - echo " - /opt/stacks/core/traefik/dynamic/${SERVER_HOSTNAME}-server-routes.yml" - echo " - /opt/stacks/core/traefik/dynamic/sablier-middleware-${SERVER_HOSTNAME}.yml" + echo "Success" + echo "║" + echo "║ Routes created on core server:" + echo "║ - ${SERVER_HOSTNAME}-server-routes.yml" + echo "║ - sablier-middleware-${SERVER_HOSTNAME}.yml" + echo "║" return 0 else - log_error "Failed to register with core server via SSH" - log_error "SSH output:" - echo "$ssh_output" + echo "Failed" + echo "║" + echo "║ ✗ Registration failed - SSH output:" + echo "$ssh_output" | sed 's/^/║ /' return 1 fi } @@ -2046,9 +2195,8 @@ deploy_sablier_stack() { # Create sablier stack directory with sudo if [ ! -d "$sablier_dir" ]; then - sudo mkdir -p "$sablier_dir" || { log_error "Failed to create $sablier_dir"; return 1; } - sudo chown -R "$ACTUAL_USER:$ACTUAL_USER" "$sablier_dir" - log_success "Created $sablier_dir" + sudo mkdir -p "$sablier_dir" >/dev/null 2>&1 || { log_error "Failed to create $sablier_dir"; return 1; } + sudo chown -R "$ACTUAL_USER:$ACTUAL_USER" "$sablier_dir" >/dev/null 2>&1 fi # Check if source files exist @@ -2057,26 +2205,21 @@ deploy_sablier_stack() { return 1 fi - # Copy stack files - log_info "Copying Sablier stack files from $REPO_DIR/docker-compose/sablier/..." - cp "$REPO_DIR/docker-compose/sablier/docker-compose.yml" "$sablier_dir/" || { log_error "Failed to copy docker-compose.yml"; return 1; } - sudo chown "$ACTUAL_USER:$ACTUAL_USER" "$sablier_dir/docker-compose.yml" - log_success "Stack files copied" + # Copy stack files silently + cp "$REPO_DIR/docker-compose/sablier/docker-compose.yml" "$sablier_dir/" 2>/dev/null || { log_error "Failed to copy docker-compose.yml"; return 1; } + sudo chown "$ACTUAL_USER:$ACTUAL_USER" "$sablier_dir/docker-compose.yml" 2>/dev/null # Process .env file from .env.example - process_stack_env "$sablier_dir" "$REPO_DIR/docker-compose/sablier" - sudo chown "$ACTUAL_USER:$ACTUAL_USER" "$sablier_dir/.env" + process_stack_env "$sablier_dir" "$REPO_DIR/docker-compose/sablier" >/dev/null 2>&1 + sudo chown "$ACTUAL_USER:$ACTUAL_USER" "$sablier_dir/.env" 2>/dev/null # Localize the docker-compose file (labels and x-dockge only) - localize_yml_file "$sablier_dir/docker-compose.yml" + localize_yml_file "$sablier_dir/docker-compose.yml" >/dev/null 2>&1 # Deploy + cd "$sablier_dir" if run_cmd --quiet docker compose up -d; then echo "Success" - echo "║" - else - echo "Failed" - echo "║" fi } @@ -2233,8 +2376,157 @@ EOF fi } +#═══════════════════════════════════════════════════════════ +# SECTION 11: UI, MENU & HELP FUNCTIONS +#═══════════════════════════════════════════════════════════ + +# Handle menu selection and set deployment flags +handle_menu_selection() { + # Menu selection loop + while true; do + # Show main menu + show_main_menu + echo -n "╚═════════════════════════════════════════════ " + read -p "Choose : " MAIN_CHOICE + + case $MAIN_CHOICE in + 1) + log_info "Selected: Install Prerequisites" + FORCE_SYSTEM_SETUP=true + DEPLOY_CORE=false + DEPLOY_INFRASTRUCTURE=false + DEPLOY_DASHBOARDS=false + SETUP_STACKS=false + break + ;; + 2) + log_info "Selected: Deploy Core Server" + # Check Docker first + if ! check_docker_installed; then + echo "║" + echo "║ ✗ Docker is not installed" + echo "║ Please run Option 1 (Install Prerequisites) first" + echo "║" + echo -n "╚═════════════════════════════════════════════ " + read -p "Press Enter to return to menu..." + continue + fi + + DEPLOY_CORE=true + DEPLOY_INFRASTRUCTURE=true + DEPLOY_DASHBOARDS=true + SETUP_STACKS=true + DEPLOY_REMOTE_SERVER=false + + # Set required variables for core deployment + set_required_vars_for_deployment "core" + + break + ;; + 3) + log_info "Selected: Deploy Additional Server" + # Check Docker first + if ! check_docker_installed; then + echo "║" + echo "║ ✗ Docker is not installed" + echo "║ Please run Option 1 (Install Prerequisites) first" + echo "║" + echo -n "╚═════════════════════════════════════════════ " + read -p "Press Enter to return to menu..." + continue + fi + + DEPLOY_CORE=false + DEPLOY_INFRASTRUCTURE=false + DEPLOY_DASHBOARDS=false + SETUP_STACKS=false + DEPLOY_REMOTE_SERVER=true + + # Set required variables for remote deployment + set_required_vars_for_deployment "remote" + + break + ;; + 4) + log_info "Selected: Install NVIDIA Drivers" + INSTALL_NVIDIA=true + DEPLOY_CORE=false + DEPLOY_INFRASTRUCTURE=false + DEPLOY_DASHBOARDS=false + SETUP_STACKS=false + break + ;; + [Qq]|[Qq]uit) + log_info "Exiting..." + exit 0 + ;; + *) + log_warning "Invalid choice '$MAIN_CHOICE'. Please select 1-4 or q to quit." + sleep 2 + continue + ;; + esac + done +} + +# Show deployment completion message with warnings +show_deployment_completion() { + # Show completion message + echo "║═════════════════════════════════════════════════════════════" + echo "║" + echo "║ Deployment Complete!" + echo "║" + echo "║ SSL Certificates may take a few minutes to be issued." + echo "║" + echo "║ Dockge https://dockge.${DOMAIN}" + echo "║ http://${SERVER_IP}:5001" + echo "║" + echo "║ Arcane https://arcane.${SERVER_HOSTNAME}.${DOMAIN}" + echo "║ http://${SERVER_IP}:3552" + echo "║" + echo "║ Homepage https://homepage.${DOMAIN}" + echo "║ http://${SERVER_IP}:3003" + echo "║" + + # Show consolidated warnings if any + if [ -n "$GLOBAL_MISSING_VARS" ] || [ -n "$TLS_ISSUES_SUMMARY" ]; then + echo "║═════════════════════════════════════════════════════════════╗" + echo "║ ⚠️ WARNING ⚠️ ║" + echo "║ The following variables were not defined ║" + echo "║ If something isn't working as expected check these first ║" + echo "║ ║" + + if [ -n "$GLOBAL_MISSING_VARS" ]; then + log_warning "Missing Environment Variables:" + echo "$GLOBAL_MISSING_VARS" + echo "║ ║" + fi + + if [ -n "$TLS_ISSUES_SUMMARY" ]; then + log_warning "TLS Configuration Issues:" + echo "$TLS_ISSUES_SUMMARY" + echo "║ ║" + fi + fi + + echo "║═════════════════════════════════════════════════════════════" + echo "║ RESOURCES" + echo "║" + echo "║ https://github.com/kelinfoxy/EZ-Homelab/blob/main/docs/Arcane-Configuration-Guide.md" + echo "║" + echo "║ Documentation: ~/EZ-Homelab/docs" + echo "║" + echo "║ Repository: https://github.com/kelinfoxy/EZ-Homelab" + echo "║ Wiki: https://github.com/kelinfoxy/EZ-Homelab/wiki" + echo "║ " + echo "╚═════════════════════════════════════════════════════════════" + echo "" + debug_log "Script completed successfully" +} + # Show help function show_help() { + echo "" echo "EZ-Homelab Setup & Deployment Script" echo "" echo "Usage: $0 [OPTIONS]" @@ -2249,6 +2541,8 @@ show_help() { echo "" echo "If no options are provided, the interactive menu will be shown." echo "" + echo "See https://github.com/kelinfoxy/EZ-Homelab for documentation" + echo "" } # Validate configuration function @@ -2346,6 +2640,10 @@ parse_args() { done } +#═══════════════════════════════════════════════════════════ +# SECTION 12: MAIN EXECUTION & SETUP +#═══════════════════════════════════════════════════════════ + # Prepare deployment environment prepare_deployment() { # Handle special menu options @@ -2385,7 +2683,7 @@ prepare_deployment() { if [ "$DOCKER_INSTALLED" = false ] || [ "$USER_IN_DOCKER_GROUP" = false ]; then log_info "Docker not fully installed or user not in docker group. Performing system setup..." ./scripts/install-prerequisites.sh - echo "" + #echo "" log_info "System setup complete. Please log out and back in, then run this script again." exit 0 else @@ -2449,12 +2747,14 @@ run_cmd() { main() { debug_log "main() called with arguments: $@" log_info "EZ-Homelab Unified Setup & Deployment Script" - clear - echo "" # Parse command line arguments parse_args "$@" + echo "" + echo "Loading... Please wait" + echo "" + if [ "$DRY_RUN" = true ]; then log_info "Dry-run mode enabled. Commands will be displayed but not executed." fi @@ -2478,194 +2778,41 @@ main() { debug_log "No existing .env file found" fi - # Menu selection loop - while true; do - # Show main menu - show_main_menu - echo -n "╚═════════════════════════════════════════════ " - read -p "Choose : " MAIN_CHOICE - - case $MAIN_CHOICE in - 1) - log_info "Selected: Install Prerequisites" - FORCE_SYSTEM_SETUP=true - DEPLOY_CORE=false - DEPLOY_INFRASTRUCTURE=false - DEPLOY_DASHBOARDS=false - SETUP_STACKS=false - break - ;; - 2) - log_info "Selected: Deploy Core Server" - - # Check Docker first - if ! check_docker_installed; then - echo "" - log_error "Docker must be installed before deploying core server" - log_info "Please run Option 1 (Install Prerequisites) first" - echo "" - read -p "Press Enter to return to main menu..." - continue - fi - - DEPLOY_CORE=true - DEPLOY_INFRASTRUCTURE=true - DEPLOY_DASHBOARDS=true - SETUP_STACKS=true - DEPLOY_REMOTE_SERVER=false - - # Set required variables for core deployment - set_required_vars_for_deployment "core" - - break - ;; - 3) - log_info "Selected: Deploy Additional Server" - echo "" - - # Check Docker first - if ! check_docker_installed; then - echo "" - log_error "Docker must be installed before deploying remote server" - log_info "Please run Option 1 (Install Prerequisites) first" - echo "" - read -p "Press Enter to return to main menu..." - continue - fi - - echo "⚠️ IMPORTANT: Deploying an additional server requires an existing core server to be already deployed." - echo "The core server provides essential services like Traefik, Authelia, and shared TLS certificates." - echo "" - - DEPLOY_CORE=false - DEPLOY_INFRASTRUCTURE=false - DEPLOY_DASHBOARDS=false - SETUP_STACKS=false - DEPLOY_REMOTE_SERVER=true - - # Set required variables for remote deployment - set_required_vars_for_deployment "remote" - - break - ;; - 4) - log_info "Selected: Install NVIDIA Drivers" - INSTALL_NVIDIA=true - DEPLOY_CORE=false - DEPLOY_INFRASTRUCTURE=false - DEPLOY_DASHBOARDS=false - SETUP_STACKS=false - break - ;; - [Qq]|[Qq]uit) - log_info "Exiting..." - exit 0 - ;; - *) - log_warning "Invalid choice '$MAIN_CHOICE'. Please select 1-4 or q to quit." - echo "" - sleep 2 - continue - ;; - esac - done + clear + echo "" + # Handle menu selection + handle_menu_selection echo "║" - # echo "╔═════════════════════════════════════════════════════════════╗" if [ "$DEPLOY_CORE" = true ]; then echo "║ CORE SERVER DEPLOYMENT" fi if [ "$DEPLOY_REMOTE_SERVER" = true ]; then - echo "║ ADDITIONAL SERVER DEPLOYMENT ║" + echo "║ ADDITIONAL SERVER DEPLOYMENT" fi - echo "║" + echo "║" # Prepare deployment environment (handles special cases like prerequisites installation) prepare_deployment - # Handle remote server deployment separately - if [ "$DEPLOY_REMOTE_SERVER" = true ]; then - # Prompt for configuration values - validate_and_prompt_variables - - # Save configuration - save_env_file - - # Reload .env file to get all variables including expanded ones - load_env_file - - # Deploy remote server - deploy_remote_server - - echo "" - log_success "Remote server deployment complete!" - exit 0 - fi - - # Prompt for configuration values + # Prompt for configuration values and save validate_and_prompt_variables - - # Save configuration save_env_file - # Validate secrets for core deployment - validate_secrets - - # Perform deployment - perform_deployment - - # Show completion message - echo "║═════════════════════════════════════════════════════════════" - echo "║" - echo "║ Deployment Complete!" - echo "║" - echo "║ SSL Certificates may take a few minutes to be issued." - echo "║" - echo "║ Dockge https://dockge.${DOMAIN}" - echo "║ http://${SERVER_IP}:5001" - echo "║" - echo "║ Arcane https://arcane.${SERVER_HOSTNAME}.${DOMAIN}" - echo "║ http://${SERVER_IP}:3552" - echo "║" - echo "║ Homepage https://homepage.${DOMAIN}" - echo "║ http://${SERVER_IP}:3003" - echo "║" - - # Show consolidated warnings if any - if [ -n "$GLOBAL_MISSING_VARS" ] || [ -n "$TLS_ISSUES_SUMMARY" ]; then - echo "║═════════════════════════════════════════════════════════════╗" - echo "║ ⚠️ WARNING ⚠️ ║" - echo "║ The following variables were not defined ║" - echo "║ If something isn't working as expected check these first ║" - echo "║ ║" - - if [ -n "$GLOBAL_MISSING_VARS" ]; then - log_warning "Missing Environment Variables:" - echo "$GLOBAL_MISSING_VARS" - echo "║ ║" - fi - - if [ -n "$TLS_ISSUES_SUMMARY" ]; then - log_warning "TLS Configuration Issues:" - echo "$TLS_ISSUES_SUMMARY" - echo "║ ║" - fi + # Deploy based on server type + if [ "$DEPLOY_REMOTE_SERVER" = true ]; then + deploy_remote_server + log_success "Remote server deployment complete!" + show_deployment_completion + else + validate_secrets + perform_deployment + show_deployment_completion fi - - echo "║═════════════════════════════════════════════════════════════" - echo "║ RESOURCES" - echo "║" - echo "║ https://github.com/kelinfoxy/EZ-Homelab/blob/main/docs/Arcane-Configuration-Guide.md" - echo "║" - echo "║ Documentation: ~/EZ-Homelab/docs" - echo "║" - echo "║ Repository: https://github.com/kelinfoxy/EZ-Homelab" - echo "║ Wiki: https://github.com/kelinfoxy/EZ-Homelab/wiki" - echo "║ " - echo "╚═════════════════════════════════════════════════════════════" - echo "" - debug_log "Script completed successfully" } -# Run main function +#═══════════════════════════════════════════════════════════ +# SCRIPT EXECUTION +#═══════════════════════════════════════════════════════════ + main "$@" \ No newline at end of file diff --git a/scripts/install-prerequisites.sh b/scripts/install-prerequisites.sh index 2a9915d..100144a 100755 --- a/scripts/install-prerequisites.sh +++ b/scripts/install-prerequisites.sh @@ -94,7 +94,8 @@ EOF # Main system setup function system_setup() { - log_info "Performing system setup..." + echo "║ Installing Prerequisites" + echo "║" # Check if running as root for system setup if [ "$EUID" -ne 0 ]; then @@ -107,90 +108,130 @@ system_setup() { # Get SERVER_IP from environment or prompt if [ -z "$SERVER_IP" ]; then - read -p "Enter the server IP address: " SERVER_IP + read -p "║ Enter the server IP address: " SERVER_IP fi # Step 1: System Update - log_info "Step 1/10: Updating system packages..." - apt-get update && apt-get upgrade -y - log_success "System updated successfully" + echo -n "║ Step 1/10: Updating system packages... " + if apt-get update >/dev/null 2>&1 && apt-get upgrade -y >/dev/null 2>&1; then + echo "✓" + else + echo "✗" + log_error "Failed to update system packages" + fi # Step 2: Install required packages - log_info "Step 2/10: Installing required packages..." - apt-get install -y curl wget git htop nano vim ufw fail2ban unattended-upgrades apt-listchanges sshpass + echo -n "║ Step 2/10: Installing required packages... " + if apt-get install -y curl wget git htop nano vim ufw fail2ban unattended-upgrades apt-listchanges sshpass gettext-base python3 >/dev/null 2>&1; then + echo "✓" + else + echo "✗" + log_error "Failed to install required packages" + fi # Step 3: Install Docker - log_info "Step 3/10: Installing Docker..." + echo -n "║ Step 3/10: Installing Docker... " if command -v docker &> /dev/null && docker --version &> /dev/null; then - log_success "Docker is already installed ($(docker --version))" + echo "✓ (already installed)" # Check if user is in docker group if ! groups "$ACTUAL_USER" 2>/dev/null | grep -q docker; then - log_info "Adding $ACTUAL_USER to docker group..." - usermod -aG docker "$ACTUAL_USER" + usermod -aG docker "$ACTUAL_USER" >/dev/null 2>&1 NEEDS_LOGOUT=true fi # Check if Docker service is running if ! systemctl is-active --quiet docker; then - log_warning "Docker service is not running, starting it..." - systemctl start docker - systemctl enable docker - log_success "Docker service started and enabled" - else - log_info "Docker service is already running" + systemctl start docker >/dev/null 2>&1 + systemctl enable docker >/dev/null 2>&1 fi else - curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh - usermod -aG docker "$ACTUAL_USER" - NEEDS_LOGOUT=true + if curl -fsSL https://get.docker.com -o /tmp/get-docker.sh >/dev/null 2>&1 && sh /tmp/get-docker.sh >/dev/null 2>&1; then + usermod -aG docker "$ACTUAL_USER" >/dev/null 2>&1 + NEEDS_LOGOUT=true + echo "✓" + else + echo "✗" + log_error "Failed to install Docker" + fi fi # Step 4: Install Docker Compose - log_info "Step 4/10: Installing Docker Compose..." + echo -n "║ Step 4/10: Installing Docker Compose... " if command -v docker-compose &> /dev/null && docker-compose --version &> /dev/null; then - log_success "Docker Compose is already installed ($(docker-compose --version))" + echo "✓ (already installed)" else - curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose - log_success "Docker Compose installed ($(docker-compose --version))" + if curl -sL "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose >/dev/null 2>&1 && chmod +x /usr/local/bin/docker-compose; then + echo "✓" + else + echo "✗" + log_error "Failed to install Docker Compose" + fi fi # Step 5: Generate shared CA for multi-server TLS - log_info "Step 5/10: Generating shared CA certificate for multi-server TLS..." - generate_shared_ca + echo -n "║ Step 5/10: Generating shared CA certificate... " + if generate_shared_ca >/dev/null 2>&1; then + echo "✓" + else + echo "✗" + log_error "Failed to generate shared CA" + fi # Step 6: Configure Docker TLS - log_info "Step 6/10: Configuring Docker TLS..." - setup_docker_tls + echo -n "║ Step 6/10: Configuring Docker TLS... " + if setup_docker_tls >/dev/null 2>&1; then + echo "✓" + else + echo "✗" + log_error "Failed to configure Docker TLS" + fi # Step 7: Configure UFW firewall - log_info "Step 7/10: Configuring firewall..." - ufw --force enable - ufw allow ssh - ufw allow 80 - ufw allow 443 - ufw allow 2376/tcp # Docker TLS port - log_success "Firewall configured" + echo -n "║ Step 7/10: Configuring firewall... " + if ufw --force enable >/dev/null 2>&1 && \ + ufw allow ssh >/dev/null 2>&1 && \ + ufw allow 80 >/dev/null 2>&1 && \ + ufw allow 443 >/dev/null 2>&1 && \ + ufw allow 2376/tcp >/dev/null 2>&1; then + echo "✓" + else + echo "✗" + log_error "Failed to configure firewall" + fi # Step 8: Configure automatic updates - log_info "Step 8/10: Configuring automatic updates..." - dpkg-reconfigure -f noninteractive unattended-upgrades - - # Step 10: Create Docker networks - log_info "Step 10/10: Creating Docker networks..." - docker network create homelab-network 2>/dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists" - docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists" - docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists" + echo -n "║ Step 8/10: Configuring automatic updates... " + if dpkg-reconfigure -f noninteractive unattended-upgrades >/dev/null 2>&1; then + echo "✓" + else + echo "✗" + log_error "Failed to configure automatic updates" + fi # Step 9: Set proper ownership - log_info "Step 9/10: Setting directory ownership..." - chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt - - log_success "System setup completed!" - echo "" - if [ "$NEEDS_LOGOUT" = true ]; then - log_info "Please log out and back in for Docker group changes to take effect." - echo "" + echo -n "║ Step 9/10: Setting directory ownership... " + if chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt >/dev/null 2>&1; then + echo "✓" + else + echo "✗" + log_error "Failed to set directory ownership" fi + + # Step 10: Create Docker networks + echo -n "║ Step 10/10: Creating Docker networks... " + docker network create homelab-network >/dev/null 2>&1 || true + docker network create traefik-network >/dev/null 2>&1 || true + docker network create media-network >/dev/null 2>&1 || true + echo "✓" + + echo "║" + echo "║ Prerequisites installation complete!" + echo "║" + + if [ "$NEEDS_LOGOUT" = true ]; then + echo "║ ⚠️ Please log out and back in then rerun the script." + echo "║" + fi + echo "╚════════════════════════════════════════════════════════════" } # Run the setup