homelab infrastructure services
1#!/bin/bash
2
3# Setup sheet station - the infrastructure machine for a sheet
4# Handles machine registry and shared configuration
5
6set -euo pipefail
7
8SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9TINSNIP_ROOT="$(dirname "$SCRIPT_DIR")"
10source "$TINSNIP_ROOT/lib/core.sh"
11source "$TINSNIP_ROOT/lib/uid.sh"
12source "$TINSNIP_ROOT/lib/registry.sh"
13source "$TINSNIP_ROOT/lib/nfs.sh"
14
15# Parameters
16NAS_SERVER="${1:-}"
17TIN_SHEET="${TIN_SHEET:-topsheet}"
18
19# Setup logging functions using shared lib
20log() {
21 log_with_prefix "Station Setup" "$@"
22}
23
24error() {
25 error_with_prefix "Station Setup" "$@"
26}
27
28warn() {
29 warn_with_prefix "Station Setup" "$@"
30}
31
32
33# Discover existing machines by scanning system UIDs (SMEP scheme)
34discover_existing_machines() {
35 local sheet_num=$(get_sheet_number "$TIN_SHEET")
36 local machines=()
37
38 log "Discovering existing machines in sheet '$TIN_SHEET'..." >&2
39
40 # Scan for users matching SMEP UID pattern
41 # SMEP Pattern: ${sheet_num}${machine_num_2digit}${env_num}${port}
42 for machine_num in {01..99}; do
43 for env_num in {0..9}; do
44 local smep_uid="${sheet_num}${machine_num}${env_num}0"
45
46 if getent passwd "$smep_uid" >/dev/null 2>&1; then
47 local username=$(getent passwd "$smep_uid" | cut -d: -f1)
48 local env_name
49
50 # Map environment numbers to names (expanded mapping)
51 case "$env_num" in
52 0) env_name="prod" ;;
53 1) env_name="test" ;;
54 2) env_name="dev" ;;
55 3) env_name="staging" ;;
56 4) env_name="demo" ;;
57 5) env_name="qa" ;;
58 6) env_name="uat" ;;
59 7) env_name="preview" ;;
60 8) env_name="canary" ;;
61 9) env_name="local" ;;
62 esac
63
64 # Extract machine name from username pattern: machine-env
65 if [[ "$username" =~ ^([^-]+)-${env_name}$ ]]; then
66 local machine_name="${BASH_REMATCH[1]}"
67 log "Found: $machine_name ($env_name) - UID $smep_uid (user: $username)" >&2
68 machines+=("$machine_name=$machine_num")
69 else
70 log "Found UID $smep_uid but couldn't parse machine name from username: $username" >&2
71 fi
72 fi
73 done
74 done
75
76 # Return unique machine mappings
77 printf '%s\n' "${machines[@]}" | sort -u
78}
79
80# Create machine registry from discovered machines
81create_machine_registry() {
82 local mount_point="/mnt/station-prod"
83 local sheet="${TIN_SHEET:-topsheet}"
84
85 # Create machines directory for per-sheet registries
86 sudo -u station-prod mkdir -p "$mount_point/data/machines"
87
88 # Create backward compatibility symlink (services → machines)
89 if [[ ! -e "$mount_point/data/services" ]]; then
90 log "Creating backward compatibility symlink: services → machines"
91 sudo -u station-prod ln -s machines "$mount_point/data/services"
92 fi
93
94 local registry_file="$mount_point/data/machines/$sheet/registry"
95
96 # Create sheet directory if needed
97 sudo -u station-prod mkdir -p "$mount_point/data/machines/$sheet"
98
99 # Only create registry if it doesn't exist (preserve existing registrations)
100 if [[ ! -f "$registry_file" ]]; then
101 log "Creating new machine registry for sheet '$sheet'"
102
103 # Discover machines
104 local machines=$(discover_existing_machines)
105
106 # Create per-sheet registry file with only station (other machines register themselves)
107 cat << EOF | sudo -u station-prod tee "$registry_file" > /dev/null
108# Machine Registry for $sheet sheet
109# Format: machine_name=machine_number (2-digit format)
110# Machine 00 is reserved for station (sheet infrastructure)
111
112station=00
113EOF
114
115 # Add discovered machines
116 if [[ -n "$machines" ]]; then
117 echo "$machines" | sudo -u station-prod tee -a "$registry_file" > /dev/null
118 fi
119
120 log "Machine registry created for sheet '$sheet' at $registry_file with $(wc -l < "$registry_file") entries"
121 else
122 log "Machine registry already exists for sheet '$sheet', preserving existing entries"
123 fi
124}
125
126# Create global sheet registry (only for topsheet sheet)
127create_sheet_registry() {
128 local mount_point="/mnt/station-prod"
129 local sheet_registry="$mount_point/data/sheets"
130
131 # Only create sheet registry for topsheet sheet
132 if [[ "$TIN_SHEET" != "topsheet" ]]; then
133 return 0
134 fi
135
136 log "Creating global sheet registry..."
137
138 # Create data directory
139 sudo -u station-prod mkdir -p "$mount_point/data"
140
141 # Create sheet registry file with a topsheet entry
142 cat << EOF | sudo -u station-prod tee "$sheet_registry" > /dev/null
143# Global Sheet Registry
144# Format: sheet_name=sheet_number
145# This registry prevents sheet number collisions
146# Only tinsnip.station-prod maintains this registry
147
148topsheet=5
149EOF
150
151 log "Global sheet registry created: $sheet_registry"
152}
153
154# Create NAS credentials storage (only for topsheet sheet)
155setup_nas_credentials() {
156 local mount_point="/mnt/station-prod"
157 local nas_creds_dir="$mount_point/data/nas-credentials"
158
159 # Only create NAS credentials storage for topsheet sheet
160 if [[ "$TIN_SHEET" != "topsheet" ]]; then
161 return 0
162 fi
163
164 log "Setting up NAS credentials storage..."
165
166 # Create credentials directory with restricted permissions
167 sudo -u station-prod mkdir -p "$nas_creds_dir"
168 sudo -u station-prod chmod 700 "$nas_creds_dir"
169
170 # Create placeholder files
171 sudo -u station-prod touch "$nas_creds_dir/README.md"
172 cat << EOF | sudo -u station-prod tee "$nas_creds_dir/README.md" > /dev/null
173# NAS Credentials Storage
174
175This directory stores SSH keys and credentials for NAS access.
176
177Structure:
178- \`<nas-server>.key\` - Private SSH keys for each NAS server
179- \`authorized_keys\` - Public keys for distribution to NAS servers
180- \`nas-servers\` - Registry of NAS servers per sheet
181
182Security:
183- Directory permissions: 700 (station-prod user only)
184- Key file permissions: 600 (when added)
185EOF
186
187 # Create NAS server registry with the server used for bootstrap
188 if [[ -n "$NAS_SERVER" ]]; then
189 local nas_registry="$nas_creds_dir/nas-servers"
190 cat << EOF | sudo -u station-prod tee "$nas_registry" > /dev/null
191# NAS Server Registry
192# Format: sheet=nas_server
193# This tracks which NAS servers are used by each sheet
194
195${TIN_SHEET}=$NAS_SERVER
196EOF
197 log "NAS server registry created: $NAS_SERVER registered"
198 fi
199
200 log "NAS credentials storage ready: $nas_creds_dir"
201}
202
203# Deploy catalog and scripts to station
204deploy_station_artifacts() {
205 local mount_point="/mnt/station-prod"
206 local tinsnip_root="$HOME/.local/opt/$TIN_SHEET.tinsnip"
207
208 log "Deploying station artifacts..."
209
210 # Create directory structure for scripts
211 sudo -u station-prod mkdir -p "$mount_point/opt/scripts"
212
213 # Note: Service catalog is maintained in separate repository
214 # at ~/.local/opt/{sheet}.service/ (e.g., dynamicalsystem.service)
215
216 log "Station artifacts deployed"
217}
218
219# Setup station user and mount using library functions
220setup_station_mount() {
221 local service_name="station"
222 local service_env="prod"
223 local service_uid="$(calculate_service_uid "$service_name" "$service_env")"
224 local service_user="${service_name}-${service_env}"
225
226 # Create service user
227 if ! create_service_user "$service_user" "$service_uid"; then
228 error "Failed to create service user $service_user"
229 fi
230
231 # Setup NFS mount
232 if ! setup_nfs_mount "$service_name" "$service_env" "$service_uid" "$NAS_SERVER" "$TIN_SHEET"; then
233 error "Failed to setup station NFS mount"
234 fi
235
236 # Setup XDG integration
237 if ! setup_xdg_integration "$service_name" "$service_env" "$TIN_SHEET"; then
238 error "Failed to setup XDG integration for station"
239 fi
240}
241
242# Main setup flow
243main() {
244 if [[ -z "$NAS_SERVER" ]]; then
245 echo "Usage: $0 <nas_server>"
246 echo ""
247 echo "Sets up the sheet station for machine registry and shared config"
248 exit 1
249 fi
250
251 local station_uid=$(calculate_service_uid "station" "prod")
252 log "Setting up station:"
253 log " Sheet: $TIN_SHEET"
254 log " NFS mount: /mnt/station-prod"
255 log " User: station-prod (UID: $station_uid)"
256
257 # Setup station mount and user (mount_nas.sh handles existence check and creation guidance)
258 setup_station_mount
259
260 # Create registries
261 create_machine_registry
262 create_sheet_registry # Only for topsheet sheet
263 setup_nas_credentials # Only for topsheet sheet
264
265 # Deploy catalog and scripts
266 deploy_station_artifacts
267
268 log "Station setup complete!"
269 if [[ "$TIN_SHEET" == "topsheet" ]]; then
270 log " Global sheet registry: /mnt/station-prod/data/sheets"
271 log " NAS credentials: /mnt/station-prod/data/nas-credentials/"
272 fi
273 log " Machine registry: /mnt/station-prod/data/machines"
274 log " Scripts: /mnt/station-prod/opt/scripts/"
275}
276
277main "$@"