homelab infrastructure services
1#!/bin/bash
2
3# Service-specific NFS mount for tinsnip services
4# Implements DEPLOYMENT_STRATEGY.md conventions
5
6set -euo pipefail
7
8# Required parameters
9TIN_SERVICE_NAME="${1:-}"
10TIN_SERVICE_ENVIRONMENT="${2:-}"
11NAS_SERVER="${3:-}"
12
13# Get script directory and source lib
14SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15source "$SCRIPT_DIR/lib.sh"
16
17log() {
18 echo "[NFS Mount] $*"
19}
20
21error() {
22 log "ERROR: $*" >&2
23 exit 1
24}
25
26usage() {
27 echo "Usage: $0 <service_name> <service_env> <nas_server>"
28 echo " service_name: tinsnip, gazette, etc."
29 echo " service_env: prod, test"
30 echo " nas_server: NAS hostname or IP"
31 echo ""
32 echo "Example: $0 tinsnip test DS412plus"
33 exit 1
34}
35
36# calculate_service_uid is now provided by lib.sh
37
38
39install_nfs_dependencies() {
40 log "Installing NFS dependencies..."
41
42 if sudo apt-get update -qq >/dev/null 2>&1; then
43 log " Package lists updated"
44 else
45 error "Failed to update package lists"
46 fi
47
48 if sudo apt-get install -y nfs-common >/dev/null 2>&1; then
49 log " NFS utilities installed"
50 else
51 error "Failed to install NFS dependencies"
52 fi
53}
54
55create_service_user() {
56 local service_user="$1"
57 local service_uid="$2"
58
59 if ! id "$service_user" &>/dev/null; then
60 log "Creating service user: $service_user (UID: $service_uid)"
61 sudo useradd -u "$service_uid" -s /bin/bash -m "$service_user"
62 else
63 log "Service user $service_user already exists"
64 fi
65}
66
67show_nfs_setup_instructions() {
68 local service_name="$1"
69 local service_env="$2"
70 local nas_server="$3"
71 local namespace="$4"
72 local service_uid="$5"
73
74 local nfs_export="/volume1/${namespace}/${service_name}/${service_env}"
75 local client_host=$(hostname -f 2>/dev/null || hostname)
76
77 echo
78 echo "============================================================"
79 echo "NFS EXPORT SETUP REQUIRED"
80 echo "============================================================"
81 echo
82 echo "Before mounting, you need to configure NFS exports on your NAS."
83 echo "SSH into your NAS ($nas_server) and run these commands:"
84 echo
85 echo "# 1. Create directory and set ownership"
86 echo "sudo mkdir -p $nfs_export"
87 echo "sudo chown ${service_uid}:${service_uid} $nfs_export"
88 echo
89 echo "# 2. Add this line to /etc/exports (use TAB between path and options):"
90 echo "${nfs_export} ${client_host}(rw,async,no_subtree_check,all_squash,anonuid=${service_uid},anongid=${service_uid})"
91 echo
92 echo "# 3. Reload NFS exports"
93 echo "sudo exportfs -ra"
94 echo "sudo exportfs -v # Verify export is active"
95 echo
96 echo "============================================================"
97 echo
98
99 # Wait for user confirmation
100 while true; do
101 read -p "Have you completed the NFS export setup on $nas_server? (y/n): " confirm
102 case $confirm in
103 [Yy]* )
104 log "Proceeding with NFS mount..."
105 break
106 ;;
107 [Nn]* )
108 echo "Please complete the NFS export setup before continuing."
109 echo "You can also use: ./machine/scripts/generate_nfs_exports.sh $service_name $service_env $namespace $client_host"
110 ;;
111 * )
112 echo "Please answer yes or no."
113 ;;
114 esac
115 done
116}
117
118mount_nfs_share() {
119 local service_name="$1"
120 local service_env="$2"
121 local nas_server="$3"
122 local mount_point="/mnt/${service_name}-${service_env}"
123
124 local namespace
125 namespace=$(get_namespace)
126 local nfs_export="/volume1/${namespace}/${service_name}/${service_env}"
127
128 # Calculate service UID for NFS setup instructions
129 local service_uid
130 service_uid=$(calculate_service_uid "$service_name" "$service_env")
131
132 log "Mounting NFS:"
133 log " Export: ${nas_server}:${nfs_export}"
134 log " Mount: ${mount_point}"
135
136 # Create mount point
137 sudo mkdir -p "$mount_point"
138
139 # Unmount if already mounted
140 if mountpoint -q "$mount_point" 2>/dev/null; then
141 log " Unmounting old $mount_point first"
142 sudo umount "$mount_point"
143 fi
144
145 # Mount NFS share
146 if ! sudo mount -t nfs "${nas_server}:${nfs_export}" "$mount_point"; then
147 echo
148 echo "❌ NFS mount failed!"
149 echo "Common issues:"
150 echo "1. NFS export not configured correctly on NAS"
151 echo "2. Wrong hostname/IP for NAS server"
152 echo "3. Directory doesn't exist or has wrong ownership on NAS"
153 echo "4. Firewall blocking NFS traffic (ports 111, 2049)"
154 echo "5. NFS service not running on NAS"
155 echo
156 echo "To troubleshoot:"
157 echo "1. Verify export exists: ssh $nas_server sudo exportfs -v"
158 echo "2. Test basic NFS: sudo mount -t nfs $nas_server:/volume1 /tmp/test"
159 echo "3. Check NAS logs for errors"
160 echo
161 error "Cannot mount NFS export ${nas_server}:${nfs_export}"
162 fi
163
164 # Verify mount
165 if ! mountpoint -q "$mount_point"; then
166 error "Mount point verification failed"
167 fi
168
169 log " NFS mount successful"
170}
171
172get_namespace() {
173 # Use namespace from environment, file, or default
174 if [[ -n "${TIN_NAMESPACE:-}" ]]; then
175 echo "$TIN_NAMESPACE"
176 elif [[ -f "/etc/tinsnip-namespace" ]]; then
177 cat "/etc/tinsnip-namespace"
178 else
179 echo "dynamicalsystem"
180 fi
181}
182
183# Check if NFS export exists and is accessible
184check_nfs_exists() {
185 local nas_server="$1"
186 local nfs_export="$2"
187
188 # First try using showmount if available
189 if command -v showmount >/dev/null 2>&1; then
190 if showmount -e "$nas_server" 2>/dev/null | grep -q "^${nfs_export}[[:space:]]"; then
191 log "NFS export found via showmount"
192 return 0
193 fi
194 fi
195
196 # Try temporary mount to check NFS export
197 local temp_mount="/tmp/nfs-check-$$"
198 mkdir -p "$temp_mount"
199
200 # Increased timeout and retries for better detection
201 if sudo mount -t nfs -o ro,soft,timeo=10,retry=3 "${nas_server}:${nfs_export}" "$temp_mount" 2>/dev/null; then
202 sudo umount "$temp_mount" 2>/dev/null || true
203 rmdir "$temp_mount" 2>/dev/null || true
204 return 0 # NFS export exists and is accessible
205 else
206 # Log why it failed for debugging
207 log " ❌ Mount test failed: ${nas_server}:${nfs_export}"
208 rmdir "$temp_mount" 2>/dev/null || true
209 return 1 # NFS export doesn't exist or can't mount
210 fi
211}
212
213# Guide user through NFS export creation using generate_nfs_exports.sh
214guide_nfs_export_creation() {
215 local service_name="$1"
216 local service_env="$2"
217 local nas_server="$3"
218 local namespace
219 namespace=$(get_namespace)
220
221 log " 📝 Generating $service_name-$service_env NFS export instructions..."
222
223 # Use generate_nfs_exports.sh to show exact commands needed
224 if ! NAS_HOST="$nas_server" "$SCRIPT_DIR/generate_nfs_exports.sh" "$service_name" "$service_env" "$namespace" "$(hostname)"; then
225 error "Failed to generate NFS export instructions"
226 fi
227
228 echo
229 log "Press Enter when you've completed the NFS export setup:"
230 read -p " $nas_server..."
231
232 # Build NFS export path for verification
233 local nfs_export="/volume1/${namespace}/${service_name}/${service_env}"
234
235 # Verify it's now accessible
236 if ! check_nfs_exists "$nas_server" "$nfs_export"; then
237 error "NFS export still not accessible: ${nas_server}:${nfs_export}. Please verify the setup."
238 fi
239
240 log "NFS export verified successfully"
241}
242
243# Set up service environment with NFS .env file and shell integration
244setup_service_environment() {
245 local service_name="$1"
246 local service_env="$2"
247 local service_uid="$3"
248 local service_user="${service_name}-${service_env}"
249 local mount_point="/mnt/${service_name}-${service_env}"
250
251 log "Setting up service environment for $service_user"
252
253 # Create the directories on the NFS mount
254 sudo -u "$service_user" mkdir -p "$mount_point"/{data,config,state,cache}
255
256 # Create service .env file on NFS mount (source of truth)
257 create_service_env_file "$service_name" "$service_env" "$service_uid"
258
259 # Update shell configs to load from NFS .env with cache fallback
260 local env_loader_code
261 env_loader_code=$(generate_service_env_loader)
262
263 sudo -u "$service_user" -i bash << 'SETUP_EOF'
264# Clear any existing environment loading code (more comprehensive cleanup)
265for config_file in ~/.bashrc ~/.profile; do
266 if [[ -f "$config_file" ]]; then
267 # Remove all tinsnip-related environment code
268 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"
269 else
270 touch "$config_file"
271 fi
272done
273SETUP_EOF
274
275 # Add environment loading code to both files using printf
276 printf '%s\n' "$env_loader_code" | sudo -u "$service_user" tee -a /home/"$service_user"/.bashrc > /dev/null
277 printf '%s\n' "$env_loader_code" | sudo -u "$service_user" tee -a /home/"$service_user"/.profile > /dev/null
278
279 log "Service environment configured:"
280 log " Primary: $mount_point/.env"
281 log " Cache: \$XDG_CACHE_HOME/tinsnip/$service_user.env"
282 log " Shell integration: .bashrc and .profile updated"
283}
284
285main() {
286 # Validate parameters
287 if [[ -z "$TIN_SERVICE_NAME" || -z "$TIN_SERVICE_ENVIRONMENT" || -z "$NAS_SERVER" ]]; then
288 usage
289 fi
290
291 # Calculate service UID using convention
292 TIN_SERVICE_UID=$(calculate_service_uid "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT")
293 SERVICE_USER="${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}"
294
295 log "Setting up NFS mount:"
296 log " Service: $TIN_SERVICE_NAME"
297 log " Environment: $TIN_SERVICE_ENVIRONMENT"
298 log " Service user: $SERVICE_USER (UID: $TIN_SERVICE_UID)"
299 log " NAS server: $NAS_SERVER"
300
301 # Install dependencies
302 install_nfs_dependencies
303
304 # Create service user with calculated UID
305 create_service_user "$SERVICE_USER" "$TIN_SERVICE_UID"
306
307 # Check if NFS export exists, guide creation if needed
308 local namespace
309 namespace=$(get_namespace)
310 local nfs_export="/volume1/${namespace}/${TIN_SERVICE_NAME}/${TIN_SERVICE_ENVIRONMENT}"
311
312 log "Checking NFS export..."
313 if ! check_nfs_exists "$NAS_SERVER" "$nfs_export"; then
314 log " ❌ ${NAS_SERVER}:${nfs_export}"
315 log "NFS export not found, guiding setup..."
316 guide_nfs_export_creation "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$NAS_SERVER"
317 else
318 log " ${NAS_SERVER}:${nfs_export}"
319 fi
320
321 # Mount the NFS share
322 mount_nfs_share "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$NAS_SERVER"
323
324 # Set up service environment (.env file and shell integration)
325 setup_service_environment "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$TIN_SERVICE_UID"
326
327 log "NFS mount setup complete!"
328 log "Mount point: /mnt/${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}"
329 log "Service user: $SERVICE_USER"
330}
331
332main "$@"