#!/bin/bash # Service-specific NFS mount for tinsnip services # Implements DEPLOYMENT_STRATEGY.md conventions set -euo pipefail # Required parameters TIN_SERVICE_NAME="${1:-}" TIN_SERVICE_ENVIRONMENT="${2:-}" NAS_SERVER="${3:-}" # Get script directory and source lib SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/lib.sh" log() { echo "[NFS Mount] $*" } error() { log "ERROR: $*" >&2 exit 1 } usage() { echo "Usage: $0 " echo " service_name: tinsnip, gazette, etc." echo " service_env: prod, test" echo " nas_server: NAS hostname or IP" echo "" echo "Example: $0 tinsnip test DS412plus" exit 1 } # calculate_service_uid is now provided by lib.sh install_nfs_dependencies() { log "Installing NFS dependencies..." if sudo apt-get update -qq >/dev/null 2>&1; then log " Package lists updated" else error "Failed to update package lists" fi if sudo apt-get install -y nfs-common >/dev/null 2>&1; then log " NFS utilities installed" else error "Failed to install NFS dependencies" fi } create_service_user() { local service_user="$1" local service_uid="$2" if ! id "$service_user" &>/dev/null; then log "Creating service user: $service_user (UID: $service_uid)" sudo useradd -u "$service_uid" -s /bin/bash -m "$service_user" else log "Service user $service_user already exists" fi } show_nfs_setup_instructions() { local service_name="$1" local service_env="$2" local nas_server="$3" local namespace="$4" local service_uid="$5" local nfs_export="/volume1/${namespace}/${service_name}/${service_env}" local client_host=$(hostname -f 2>/dev/null || hostname) echo echo "============================================================" echo "NFS EXPORT SETUP REQUIRED" echo "============================================================" echo echo "Before mounting, you need to configure NFS exports on your NAS." echo "SSH into your NAS ($nas_server) and run these commands:" echo echo "# 1. Create directory and set ownership" echo "sudo mkdir -p $nfs_export" echo "sudo chown ${service_uid}:${service_uid} $nfs_export" echo echo "# 2. Add this line to /etc/exports (use TAB between path and options):" echo "${nfs_export} ${client_host}(rw,async,no_subtree_check,all_squash,anonuid=${service_uid},anongid=${service_uid})" echo echo "# 3. Reload NFS exports" echo "sudo exportfs -ra" echo "sudo exportfs -v # Verify export is active" echo echo "============================================================" echo # Wait for user confirmation while true; do read -p "Have you completed the NFS export setup on $nas_server? (y/n): " confirm case $confirm in [Yy]* ) log "Proceeding with NFS mount..." break ;; [Nn]* ) echo "Please complete the NFS export setup before continuing." echo "You can also use: ./machine/scripts/generate_nfs_exports.sh $service_name $service_env $namespace $client_host" ;; * ) echo "Please answer yes or no." ;; esac done } mount_nfs_share() { local service_name="$1" local service_env="$2" local nas_server="$3" local mount_point="/mnt/${service_name}-${service_env}" local namespace namespace=$(get_namespace) local nfs_export="/volume1/${namespace}/${service_name}/${service_env}" # Calculate service UID for NFS setup instructions local service_uid service_uid=$(calculate_service_uid "$service_name" "$service_env") log "Mounting NFS:" log " Export: ${nas_server}:${nfs_export}" log " Mount: ${mount_point}" # Create mount point sudo mkdir -p "$mount_point" # Unmount if already mounted if mountpoint -q "$mount_point" 2>/dev/null; then log " Unmounting old $mount_point first" sudo umount "$mount_point" fi # Mount NFS share if ! sudo mount -t nfs "${nas_server}:${nfs_export}" "$mount_point"; then echo echo "❌ NFS mount failed!" echo "Common issues:" echo "1. NFS export not configured correctly on NAS" echo "2. Wrong hostname/IP for NAS server" echo "3. Directory doesn't exist or has wrong ownership on NAS" echo "4. Firewall blocking NFS traffic (ports 111, 2049)" echo "5. NFS service not running on NAS" echo echo "To troubleshoot:" echo "1. Verify export exists: ssh $nas_server sudo exportfs -v" echo "2. Test basic NFS: sudo mount -t nfs $nas_server:/volume1 /tmp/test" echo "3. Check NAS logs for errors" echo error "Cannot mount NFS export ${nas_server}:${nfs_export}" fi # Verify mount if ! mountpoint -q "$mount_point"; then error "Mount point verification failed" fi log " NFS mount successful" } get_namespace() { # Use namespace from environment, file, or default if [[ -n "${TIN_NAMESPACE:-}" ]]; then echo "$TIN_NAMESPACE" elif [[ -f "/etc/tinsnip-namespace" ]]; then cat "/etc/tinsnip-namespace" else echo "dynamicalsystem" fi } # Check if NFS export exists and is accessible check_nfs_exists() { local nas_server="$1" local nfs_export="$2" # First try using showmount if available if command -v showmount >/dev/null 2>&1; then if showmount -e "$nas_server" 2>/dev/null | grep -q "^${nfs_export}[[:space:]]"; then log "NFS export found via showmount" return 0 fi fi # Try temporary mount to check NFS export local temp_mount="/tmp/nfs-check-$$" mkdir -p "$temp_mount" # Increased timeout and retries for better detection if sudo mount -t nfs -o ro,soft,timeo=10,retry=3 "${nas_server}:${nfs_export}" "$temp_mount" 2>/dev/null; then sudo umount "$temp_mount" 2>/dev/null || true rmdir "$temp_mount" 2>/dev/null || true return 0 # NFS export exists and is accessible else # Log why it failed for debugging log " ❌ Mount test failed: ${nas_server}:${nfs_export}" rmdir "$temp_mount" 2>/dev/null || true return 1 # NFS export doesn't exist or can't mount fi } # Guide user through NFS export creation using generate_nfs_exports.sh guide_nfs_export_creation() { local service_name="$1" local service_env="$2" local nas_server="$3" local namespace namespace=$(get_namespace) log " 📝 Generating $service_name-$service_env NFS export instructions..." # Use generate_nfs_exports.sh to show exact commands needed if ! NAS_HOST="$nas_server" "$SCRIPT_DIR/generate_nfs_exports.sh" "$service_name" "$service_env" "$namespace" "$(hostname)"; then error "Failed to generate NFS export instructions" fi echo log "Press Enter when you've completed the NFS export setup:" read -p " $nas_server..." # Build NFS export path for verification local nfs_export="/volume1/${namespace}/${service_name}/${service_env}" # Verify it's now accessible if ! check_nfs_exists "$nas_server" "$nfs_export"; then error "NFS export still not accessible: ${nas_server}:${nfs_export}. Please verify the setup." fi log "NFS export verified successfully" } # Set up service environment with NFS .env file and shell integration setup_service_environment() { local service_name="$1" local service_env="$2" local service_uid="$3" local service_user="${service_name}-${service_env}" local mount_point="/mnt/${service_name}-${service_env}" log "Setting up service environment for $service_user" # Create the directories on the NFS mount sudo -u "$service_user" mkdir -p "$mount_point"/{data,config,state,cache} # Create service .env file on NFS mount (source of truth) create_service_env_file "$service_name" "$service_env" "$service_uid" # Update shell configs to load from NFS .env with cache fallback local env_loader_code env_loader_code=$(generate_service_env_loader) sudo -u "$service_user" -i bash << 'SETUP_EOF' # Clear any existing environment loading code (more comprehensive cleanup) for config_file in ~/.bashrc ~/.profile; do if [[ -f "$config_file" ]]; then # Remove all tinsnip-related environment code grep -v -E "(Tinsnip service environment|SERVICE_ENV_FILE|TIN_SERVICE_ENV_CACHE|XDG_.*_HOME.*=|export XDG_)" "$config_file" > "${config_file}.tmp" 2>/dev/null && mv "${config_file}.tmp" "$config_file" || touch "$config_file" else touch "$config_file" fi done SETUP_EOF # Add environment loading code to both files using printf printf '%s\n' "$env_loader_code" | sudo -u "$service_user" tee -a /home/"$service_user"/.bashrc > /dev/null printf '%s\n' "$env_loader_code" | sudo -u "$service_user" tee -a /home/"$service_user"/.profile > /dev/null log "Service environment configured:" log " Primary: $mount_point/.env" log " Cache: \$XDG_CACHE_HOME/tinsnip/$service_user.env" log " Shell integration: .bashrc and .profile updated" } main() { # Validate parameters if [[ -z "$TIN_SERVICE_NAME" || -z "$TIN_SERVICE_ENVIRONMENT" || -z "$NAS_SERVER" ]]; then usage fi # Calculate service UID using convention TIN_SERVICE_UID=$(calculate_service_uid "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT") SERVICE_USER="${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}" log "Setting up NFS mount:" log " Service: $TIN_SERVICE_NAME" log " Environment: $TIN_SERVICE_ENVIRONMENT" log " Service user: $SERVICE_USER (UID: $TIN_SERVICE_UID)" log " NAS server: $NAS_SERVER" # Install dependencies install_nfs_dependencies # Create service user with calculated UID create_service_user "$SERVICE_USER" "$TIN_SERVICE_UID" # Check if NFS export exists, guide creation if needed local namespace namespace=$(get_namespace) local nfs_export="/volume1/${namespace}/${TIN_SERVICE_NAME}/${TIN_SERVICE_ENVIRONMENT}" log "Checking NFS export..." if ! check_nfs_exists "$NAS_SERVER" "$nfs_export"; then log " ❌ ${NAS_SERVER}:${nfs_export}" log "NFS export not found, guiding setup..." guide_nfs_export_creation "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$NAS_SERVER" else log " ${NAS_SERVER}:${nfs_export}" fi # Mount the NFS share mount_nfs_share "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$NAS_SERVER" # Set up service environment (.env file and shell integration) setup_service_environment "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$TIN_SERVICE_UID" log "NFS mount setup complete!" log "Mount point: /mnt/${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}" log "Service user: $SERVICE_USER" } main "$@"