homelab infrastructure services
1#!/bin/bash
2
3# Setup a complete service environment with NFS and rootless Docker
4# Implements DEPLOYMENT_STRATEGY.md conventions
5
6set -euo pipefail
7
8# Parse parameters and flags
9TIN_SERVICE_NAME="${1:-}"
10TIN_SERVICE_ENVIRONMENT="${2:-}"
11NAS_SERVER="${3:-}"
12PROVIDED_UID="${4:-}" # Optional pre-calculated UID to avoid duplicate warnings
13SKIP_NAS=false
14
15# Check for --skip-nas flag in fourth or fifth parameter
16if [[ "${4:-}" == "--skip-nas" || "${5:-}" == "--skip-nas" ]]; then
17 SKIP_NAS=true
18fi
19
20SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21TINSNIP_ROOT="$(dirname "$SCRIPT_DIR")"
22
23# Source new library modules
24source "$TINSNIP_ROOT/lib/core.sh"
25source "$TINSNIP_ROOT/lib/uid.sh"
26source "$TINSNIP_ROOT/lib/nfs.sh"
27source "$TINSNIP_ROOT/lib/docker.sh"
28source "$TINSNIP_ROOT/lib/metadata.sh"
29
30log() {
31 log_with_prefix "Service Setup" "$@"
32}
33
34error() {
35 error_with_prefix "Service Setup" "$@"
36}
37
38usage() {
39 echo "Usage: $0 <service_name> <service_env> <nas_server> [uid] [--skip-nas]"
40 echo ""
41 echo "Sets up a complete service environment with:"
42 echo " - Service user with UID convention"
43 echo " - NFS mount to /mnt/<service>-<environment> (with automatic NAS setup)"
44 echo " - XDG directory integration"
45 echo " - Rootless Docker with privileged ports"
46 echo ""
47 echo "Parameters:"
48 echo " service_name: tinsnip, gazette, etc."
49 echo " service_env: prod, test, dev, staging"
50 echo " nas_server: NAS hostname or IP"
51 echo " uid: Optional pre-calculated UID (to avoid duplicate warnings)"
52 echo " --skip-nas: Skip automated NAS setup (requires manual configuration)"
53 echo ""
54 echo "Examples:"
55 echo " $0 tinsnip test DS412plus # With automated NAS setup"
56 echo " $0 lldap prod 192.168.1.100 --skip-nas # Manual NAS setup required"
57 echo " $0 gateway prod 192.168.1.100 50500 # With pre-calculated UID"
58 exit 1
59}
60
61# calculate_service_uid is now provided by lib.sh
62
63check_prerequisites() {
64 # Check Ubuntu
65 if [[ ! -f /etc/os-release ]] || ! grep -q "Ubuntu" /etc/os-release; then
66 error "This script requires Ubuntu"
67 fi
68
69 # Check sudo access
70 if ! groups | grep -q sudo; then
71 error "Current user must have sudo access"
72 fi
73
74 # Check not running as root or service user
75 if [[ $EUID -eq 0 ]]; then
76 error "Do not run as root. Run as a regular user with sudo access."
77 fi
78
79 local service_user="${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}"
80 if [[ "$USER" == "$service_user" ]]; then
81 error "Do not run as the service user ($service_user). Run as a regular admin user."
82 fi
83}
84
85main() {
86 # Validate parameters
87 if [[ -z "$TIN_SERVICE_NAME" || -z "$TIN_SERVICE_ENVIRONMENT" || -z "$NAS_SERVER" ]]; then
88 usage
89 fi
90
91 # Guard against explicit station creation
92 if [[ "$TIN_SERVICE_NAME" == "station" ]]; then
93 error "Station is infrastructure and cannot be created as a service. It will be automatically created when needed."
94 fi
95
96 # Calculate service details (use provided UID if available to avoid duplicate warning)
97 if [[ -n "$PROVIDED_UID" && "$PROVIDED_UID" =~ ^[0-9]+$ ]]; then
98 TIN_SERVICE_UID="$PROVIDED_UID"
99 else
100 TIN_SERVICE_UID=$(calculate_service_uid "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT")
101 fi
102 SERVICE_USER="${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}"
103
104 log "Service Environment Setup"
105 log "========================="
106 log "Service: $TIN_SERVICE_NAME"
107 log "Environment: $TIN_SERVICE_ENVIRONMENT"
108 log "User: $SERVICE_USER (UID: $TIN_SERVICE_UID)"
109 log "NAS: $NAS_SERVER"
110 if [[ "$SKIP_NAS" == "true" ]]; then
111 log "NAS Setup: Manual (--skip-nas flag used)"
112 else
113 log "NAS Setup: Automated (if SSH keys available)"
114 fi
115 echo
116
117 # Check prerequisites
118 check_prerequisites
119
120 # Check for username collision (idempotent - allow if UID matches)
121 if id "$SERVICE_USER" &>/dev/null 2>&1; then
122 local existing_uid=$(id -u "$SERVICE_USER")
123
124 if [[ "$existing_uid" == "$TIN_SERVICE_UID" ]]; then
125 log "User '$SERVICE_USER' already exists with correct UID $existing_uid (idempotent)"
126 # User exists with correct UID - continue with setup
127 else
128 # UID mismatch - this is a real conflict
129 error "User '$SERVICE_USER' already exists with conflicting UID $existing_uid (expected: $TIN_SERVICE_UID)"
130 echo "" >&2
131 echo "This may be from:" >&2
132 echo " - A different sheet using the same service/environment name" >&2
133 echo " - A previous deployment with different UID" >&2
134 echo "" >&2
135 echo "To avoid collision, choose a unique service name:" >&2
136 echo " tin machine ${TIN_SERVICE_NAME}-${TIN_SHEET} ${TIN_SERVICE_ENVIRONMENT}" >&2
137 local sheet_num=$(get_sheet_number "${TIN_SHEET:-topsheet}" 2>/dev/null || echo "X")
138 echo " tin machine ${TIN_SERVICE_NAME}-${sheet_num} ${TIN_SERVICE_ENVIRONMENT}" >&2
139 echo " tin machine $(echo ${TIN_SHEET:-topsheet} | cut -d. -f1)-${TIN_SERVICE_NAME} ${TIN_SERVICE_ENVIRONMENT}" >&2
140 echo "" >&2
141 echo "Or remove the existing user:" >&2
142 echo " sudo userdel -r $SERVICE_USER" >&2
143 exit 1
144 fi
145 fi
146
147 # Ensure station exists first
148 local sheet="${TIN_SHEET:-topsheet}"
149 log "Checking $sheet station..."
150 if ! "$SCRIPT_DIR/setup_station.sh" "$NAS_SERVER"; then
151 error "Failed to setup sheet station"
152 fi
153
154 # Proceeding with setup
155
156 # Step 1: Mount NFS and create service user
157 log "Step 1: Setting up NFS mount and service user..."
158
159 # Create service user
160 if ! create_service_user "$SERVICE_USER" "$TIN_SERVICE_UID"; then
161 error "Failed to create service user $SERVICE_USER"
162 fi
163
164 # Setup NFS mount (unless skipping NAS setup)
165 if [[ "$SKIP_NAS" != "true" ]]; then
166 if ! setup_nfs_mount "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$TIN_SERVICE_UID" "$NAS_SERVER" "$sheet"; then
167 error "Failed to setup NFS mount"
168 fi
169
170 # Setup XDG integration
171 if ! setup_xdg_integration "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$sheet"; then
172 error "Failed to setup XDG integration"
173 fi
174
175 # Create machine metadata in station-prod
176 log "Creating machine metadata in station-prod..."
177 if ! create_machine_metadata "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT" "$TIN_SERVICE_UID"; then
178 error "Failed to create machine metadata"
179 fi
180
181 # Create .machine symlink from mount to station-prod
182 log "Creating .machine symlink..."
183 if ! create_machine_symlink "$TIN_SERVICE_NAME" "$TIN_SERVICE_ENVIRONMENT"; then
184 error "Failed to create .machine symlink"
185 fi
186
187 # Create environment loader script for automatic sourcing on login
188 log "Creating environment loader script..."
189 if ! create_service_env_loader_script "$SERVICE_USER"; then
190 warn "Failed to create environment loader script"
191 fi
192 fi
193
194 # Step 2: Install rootless Docker
195 log "Step 2: Installing rootless Docker..."
196 if ! install_docker_for_user "$SERVICE_USER"; then
197 error "Failed to install Docker"
198 fi
199
200 log ""
201 log "Machine setup complete!"
202 log ""
203 log "Machine environment details:"
204 log " Machine: ${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}"
205 log " User: $SERVICE_USER (UID: $TIN_SERVICE_UID)"
206 log " NFS mount: /mnt/${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}"
207 log " XDG integration: ~/.local/{state,share,config}/$sheet/@$TIN_SERVICE_NAME"
208 if [[ "$SKIP_NAS" == "false" ]]; then
209 log " NAS exports: Automatically configured (if SSH keys available)"
210 else
211 log " NAS exports: Manual configuration required (--skip-nas used)"
212 fi
213 log ""
214 log "To use this environment:"
215 log " sudo -u $SERVICE_USER -i"
216 log " cd /mnt/${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}"
217 log " docker run hello-world"
218 log ""
219 log "To deploy services to this machine:"
220 log " # Using modern CLI (recommended):"
221 log " tin service deploy ${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT} <service-name>"
222 log " # Examples:"
223 log " tin service deploy ${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT} lldap"
224 log " tin service deploy ${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT} caddy"
225 log ""
226 log " # Or manually as service user:"
227 log " sudo -u $SERVICE_USER -i"
228 log " cd /mnt/${TIN_SERVICE_NAME}-${TIN_SERVICE_ENVIRONMENT}/service/<service-name>"
229 log " docker compose up -d"
230}
231
232main "$@"