52c75d7a / docker-entrypoint.sh
autoface's picture
Fix the script path in docker-entrypoint.sh by changing "scripts/utils/restic-restore.sh" to "scripts/utils/restic-restore.sh" to ensure a consistent directory structure.
2889faa
#!/bin/bash
set -e
# Log function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [MAIN] $*"
}
# Array to store background process PIDs
declare -a SERVICE_PIDS=()
# Function to extract service name from script filename
get_service_name() {
local script_name="$1"
# Remove -start.sh suffix and convert to uppercase for env var
echo "$script_name" | sed 's/-start\.sh$//' | tr '[:lower:]' '[:upper:]' | tr '-' '_'
}
# Define start directory path
start_dir="script/start"
# Function to start persistence service synchronously (BLOCKING)
start_persistence_service() {
local persistence_script="$start_dir/persistence-start.sh"
if [[ -f "$persistence_script" ]] && [[ "${PERSISTENCE_ENABLED:-false}" == "true" ]]; then
log "=== STARTING PERSISTENCE SERVICE (SYNCHRONOUS) ==="
log "This is a BLOCKING operation that must complete before other services start"
if "$persistence_script"; then
log "✓ Persistence service completed successfully"
log "Data restoration and verification completed"
return 0
else
log "✗ Persistence service failed"
log "ERROR: Cannot proceed with other services due to persistence failure"
exit 1
fi
else
if [[ "${PERSISTENCE_ENABLED:-false}" == "true" ]]; then
log "WARNING: Persistence enabled but script not found: $persistence_script"
else
log "Persistence service is disabled"
fi
return 0
fi
}
# Function to perform restic auto-restore BEFORE any services start
perform_restic_restore() {
log "=== CHECKING FOR RESTIC AUTO-RESTORE ==="
# Prepare restic environment first
local user_home="${HOME:-/home/user}"
local config_file="$user_home/config/restic.conf"
# Load restic configuration BEFORE checking RESTIC_AUTO_RESTORE
if [[ -f "$config_file" ]]; then
log "Loading restic config from: $config_file"
# Load config with environment variable precedence
local temp_config="/tmp/restic_config_$$"
sed 's/^\([^#]*\)=\(.*\)$/\1=${\1:-\2}/' "$config_file" > "$temp_config"
source "$temp_config"
rm -f "$temp_config"
log "Configuration loaded successfully"
else
log "No restic configuration file found: $config_file"
fi
# Now check if restic auto-restore is enabled (after loading config)
if [[ "${RESTIC_AUTO_RESTORE:-false}" != "true" ]]; then
log "Restic auto-restore is disabled"
return 0
fi
log "Restic auto-restore is enabled - performing data restoration BEFORE services start"
# Set up restic environment
export RESTIC_REPOSITORY="${RESTIC_REPOSITORY_PATH:-}"
export RESTIC_PASSWORD="${RESTIC_PASSWORD:-}"
export RESTIC_CACHE_DIR="${RESTIC_CACHE_DIR:-$user_home/.cache/restic}"
# Check if restic is available
if ! command -v restic &> /dev/null; then
log "WARNING: Restic command not found, skipping auto-restore"
log "Install restic to enable backup/restore functionality"
return 0
fi
# Check if repository is accessible
if ! restic snapshots > /dev/null 2>&1; then
log "WARNING: Cannot access restic repository, skipping auto-restore"
log "This might be the first startup or repository is not yet initialized"
log "To create a repository, set RESTIC_ENABLED=true and restart the service"
return 0
fi
# Check if we have snapshots available - simplified to just check if any exist
if ! restic snapshots --json | jq -e '. | length > 0' > /dev/null 2>&1; then
log "No snapshots available for restore, skipping auto-restore"
return 0
fi
# Use the restore script for proper restoration with latest snapshot
local restore_script="script/utils/restic-restore.sh"
if [[ -x "$restore_script" ]]; then
log "Using restic restore script for data restoration with latest snapshot"
# Simplified arguments - always use latest snapshot
local args=("--mode" "${RESTIC_RESTORE_MODE:-safe}" "--force")
# Set restore target path
local restore_target
if [[ "${RESTIC_RESTORE_MODE:-safe}" == "safe" ]]; then
restore_target="${RESTIC_RESTORE_TARGET:-/tmp/restic_restore_$(date +%s)}"
else
# For replace/direct mode, use parent directory of backup paths as target
local backup_parent
backup_parent=$(dirname "${RESTIC_BACKUP_PATHS:-/home/user}")
restore_target="${RESTIC_RESTORE_TARGET:-$backup_parent}"
fi
[[ -n "$restore_target" ]] && args+=("--target" "$restore_target")
# Add include paths if specified
if [[ -n "${RESTIC_RESTORE_INCLUDE_PATHS:-}" ]]; then
IFS=':' read -ra paths <<< "$RESTIC_RESTORE_INCLUDE_PATHS"
for path in "${paths[@]}"; do
args+=("--include" "$path")
done
fi
# Always use latest - no need to pass snapshot ID
log "Executing restic restore: $restore_script ${args[*]} (using latest snapshot)"
if "$restore_script" "${args[@]}"; then
log "✓ Restic auto-restore completed successfully"
log "Data restoration finished - services can now start safely"
# For safe mode, provide guidance on next steps
if [[ "${RESTIC_RESTORE_MODE:-safe}" == "safe" ]]; then
log "Safe mode: Data restored to temporary location: $restore_target"
log "Please verify and manually copy data if needed"
fi
return 0
else
log "✗ Restic auto-restore failed"
return 1
fi
else
log "ERROR: Restic restore script not found: $restore_script"
return 1
fi
}
# Function to start optional services automatically (AFTER persistence)
start_optional_services() {
log "Auto-discovering and starting optional services..."
# Process all start scripts except core service and persistence
for script in "$start_dir"/*-start.sh; do
if [[ -f "$script" ]]; then
local script_name=$(basename "$script")
# Skip core service (handled separately) and persistence (handled above)
if [[ "$script_name" == "core-service-start.sh" || "$script_name" == "persistence-start.sh" ]]; then
continue
fi
# Extract service name and construct environment variable name
local service_name=$(echo "$script_name" | sed 's/-start\.sh$//')
local env_var_name=$(get_service_name "$script_name")_ENABLED
# Get the environment variable value (indirect variable expansion)
local is_enabled="${!env_var_name:-false}"
log "Processing service: $service_name (env: $env_var_name, enabled: $is_enabled)"
if [[ "$is_enabled" == "true" ]]; then
log "Starting $service_name..."
"$script" &
local pid=$!
SERVICE_PIDS+=($pid)
log "$service_name started with PID: $pid"
else
log "$service_name is disabled"
fi
fi
done
if [[ ${#SERVICE_PIDS[@]} -gt 0 ]]; then
log "Started ${#SERVICE_PIDS[@]} optional services with PIDs: ${SERVICE_PIDS[*]}"
else
log "No optional services were started"
fi
}
# Check for backup service conflicts (persistence vs restic)
check_backup_service_conflicts() {
if [[ "${PERSISTENCE_ENABLED:-false}" == "true" && "${RESTIC_ENABLED:-false}" == "true" ]]; then
log "ERROR: Cannot enable both persistence and restic services simultaneously!"
log "ERROR: Please choose one backup service:"
log "ERROR: - Use persistence: export PERSISTENCE_ENABLED=true RESTIC_ENABLED=false"
log "ERROR: - Use restic: export PERSISTENCE_ENABLED=false RESTIC_ENABLED=true"
exit 1
fi
}
# Load service configurations to ensure consistency with actual startup behavior
load_service_configs() {
# Load restic config if exists (same logic as in perform_restic_restore)
local user_home="${HOME:-/home/user}"
local restic_config="$user_home/config/restic.conf"
if [[ -f "$restic_config" ]]; then
local temp_config="/tmp/restic_config_display_$$"
sed 's/^\([^#]*\)=\(.*\)$/\1=${\1:-\2}/' "$restic_config" > "$temp_config"
source "$temp_config"
rm -f "$temp_config"
fi
# Load persistence config if exists
local persistence_config="$user_home/config/persistence.conf"
if [[ -f "$persistence_config" ]]; then
local temp_config="/tmp/persistence_config_display_$$"
sed 's/^\([^#]*\)=\(.*\)$/\1=${\1:-\2}/' "$persistence_config" > "$temp_config"
source "$temp_config"
rm -f "$temp_config"
fi
}
# Load service configurations first
load_service_configs
# Display service status summary
log "Service configuration:"
for script in "$start_dir"/*-start.sh; do
if [[ -f "$script" ]]; then
script_name=$(basename "$script")
if [[ "$script_name" != "core-service-start.sh" ]]; then
service_name=$(echo "$script_name" | sed 's/-start\.sh$//')
env_var_name=$(get_service_name "$script_name")_ENABLED
is_enabled="${!env_var_name:-false}"
log " $(echo "$service_name" | sed 's/\b\w/\U&/g'): $is_enabled"
fi
fi
done
# Check for backup service conflicts BEFORE starting any services
log "Checking for backup service conflicts..."
check_backup_service_conflicts
# PHASE 1: Start persistence service SYNCHRONOUSLY (BLOCKING)
log "=== PHASE 1: PERSISTENCE AND DATA RESTORATION (SYNCHRONOUS) ==="
start_persistence_service
# Perform restic auto-restore BEFORE any other services start
perform_restic_restore || {
log "ERROR: Data restoration failed - cannot proceed with service startup"
exit 1
}
# PHASE 2: Start optional services (AFTER persistence completes)
log "=== PHASE 2: OPTIONAL SERVICES ==="
log "Starting optional services (persistence completed)..."
start_optional_services
# PHASE 3: Start core service (required, must be last)
log "=== PHASE 3: CORE SERVICE ==="
log "Starting core service (all dependencies ready)..."
if [[ -f "$start_dir/core-service-start.sh" ]]; then
log "Found core service startup script, executing..."
exec "$start_dir/core-service-start.sh"
else
log "ERROR: Core service startup script not found: $start_dir/core-service-start.sh"
log "Please ensure you have implemented your core service according to the template documentation."
log "See docs/DEVELOPMENT.md for instructions on implementing your core service."
exit 1
fi