homelab infrastructure services
1#!/bin/bash
2# Core tinsnip shared library functions
3
4# Color constants
5RED='\033[0;31m'
6GREEN='\033[0;32m'
7YELLOW='\033[1;33m'
8NC='\033[0m'
9
10# Shared logging functions with customizable prefix
11log_with_prefix() {
12 local prefix="$1"
13 shift
14 echo -e "${GREEN}[$prefix]${NC} $*" >&2
15}
16
17error_with_prefix() {
18 local prefix="$1"
19 shift
20 echo -e "${RED}[ERROR]${NC} $*" >&2
21 exit 1
22}
23
24warn_with_prefix() {
25 local prefix="$1"
26 shift
27 echo -e "${YELLOW}[WARNING]${NC} $*" >&2
28}
29
30# Get current sheet (from environment, file, or default)
31get_sheet() {
32 # Use sheet from environment, file, or default
33 if [[ -n "${TIN_SHEET:-}" ]]; then
34 echo "$TIN_SHEET"
35 elif [[ -f "/etc/tinsnip-sheet" ]]; then
36 cat "/etc/tinsnip-sheet"
37 else
38 echo "topsheet"
39 fi
40}
41
42# Update shell configuration files (.bashrc or .profile) with environment variables
43# Usage: update_shell_config username config_file "VAR1=value1" "VAR2=value2" ...
44update_shell_config() {
45 local username="$1"
46 local config_file="$2"
47 shift 2
48 local env_vars=("$@")
49
50 sudo -u "$username" -i bash << EOF
51# Remove any existing environment variables to avoid duplicates
52grep -v "$(printf "%s\\\\|" "${env_vars[@]}" | sed 's/=.*/=/g' | sed 's/\\\\|$//')" ~/$config_file > ~/${config_file}.tmp 2>/dev/null && mv ~/${config_file}.tmp ~/$config_file || touch ~/$config_file
53
54# Add environment variables
55cat >> ~/$config_file << 'CONFIG_EOF'
56
57# Environment variables managed by tinsnip
58$(printf "export %s\n" "${env_vars[@]}")
59CONFIG_EOF
60EOF
61}
62
63# Create service environment file on NFS mount with all variables needed by service
64create_service_env_file() {
65 local service_name="$1"
66 local service_env="$2"
67 local service_uid="$3"
68 local mount_point="/mnt/${service_name}-${service_env}"
69 local sheet="${TIN_SHEET:-topsheet}"
70
71 local env_file="$mount_point/.env"
72
73 log_with_prefix "Service Env" "Creating $env_file"
74
75 # Create the .env file with all service variables
76 sudo -u "${service_name}-${service_env}" tee "$env_file" > /dev/null << EOF
77# Tinsnip service environment variables
78# Generated automatically - do not edit manually
79TIN_SERVICE_NAME=$service_name
80TIN_SERVICE_ENVIRONMENT=$service_env
81TIN_SERVICE_UID=$service_uid
82TIN_SHEET=$sheet
83
84# XDG Base Directory variables (NFS-backed)
85XDG_DATA_HOME=$mount_point/data
86XDG_CONFIG_HOME=$mount_point/config
87XDG_STATE_HOME=$mount_point/state
88
89# Docker environment (set by install_docker.sh if applicable)
90# These will be added by Docker setup process
91EOF
92
93 log_with_prefix "Service Env" "Created service environment file"
94}
95
96# Create service environment loader script and integrate with shell configs
97# This creates a standalone script file to avoid shell expansion issues
98create_service_env_loader_script() {
99 local service_user="$1"
100 local script_path="/home/$service_user/.tinsnip-env-loader.sh"
101
102 log_with_prefix "Env Loader" "Creating environment loader script: $script_path"
103
104 # Create environment loader for machine metadata (post-ACT-2)
105 sudo bash -c "cat > '$script_path'" << 'EOF'
106#!/bin/bash
107# Tinsnip machine environment loader (ACT-2: centralized metadata)
108export XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
109
110# Load machine environment variables from centralized metadata
111# Pattern: ^.+-[^-]+$ matches machinename-env (machine can have dashes, env cannot)
112if [[ "$(whoami)" =~ ^.+-[^-]+$ ]]; then
113 TIN_MACHINE_ENV_FILE="/mnt/$(whoami)/.machine/machine.env"
114 TIN_MACHINE_ENV_CACHE="$XDG_CACHE_HOME/tinsnip/$(whoami).env"
115
116 if [[ -f "$TIN_MACHINE_ENV_FILE" ]]; then
117 source "$TIN_MACHINE_ENV_FILE"
118 mkdir -p "$(dirname "$TIN_MACHINE_ENV_CACHE")"
119 cp "$TIN_MACHINE_ENV_FILE" "$TIN_MACHINE_ENV_CACHE" 2>/dev/null || true
120 elif [[ -f "$TIN_MACHINE_ENV_CACHE" ]]; then
121 source "$TIN_MACHINE_ENV_CACHE"
122 fi
123
124 # Export essential variables for Docker and tinsnip
125 if [[ -n "${TIN_MACHINE_NAME:-}" ]]; then
126 export TIN_MACHINE_NAME TIN_MACHINE_ENVIRONMENT TIN_SERVICE_UID TIN_SHEET
127 export DOCKER_HOST
128
129 # Set XDG_RUNTIME_DIR if not already set (needed for Docker socket access)
130 if [[ -z "${XDG_RUNTIME_DIR:-}" ]]; then
131 export XDG_RUNTIME_DIR="/run/user/${TIN_SERVICE_UID}"
132 fi
133 fi
134fi
135EOF
136
137 # Fix ownership and permissions
138 sudo chown "$service_user:$service_user" "$script_path"
139 sudo chmod 755 "$script_path"
140
141 # Debug: Check what the actual permissions are
142 log_with_prefix "Debug" "File permissions after setting:"
143 sudo ls -la "$script_path" || true
144
145 # Validate the loader script syntax (using root to avoid permission issues)
146 if ! sudo bash -n "$script_path" 2>/tmp/syntax_error.log; then
147 warn_with_prefix "Env Loader" "⚠️ Syntax error in loader script!"
148 log_with_prefix "Debug" "Syntax error details:"
149 sudo cat /tmp/syntax_error.log 2>/dev/null || true
150 log_with_prefix "Debug" "Script content:"
151 sudo head -10 "$script_path" 2>/dev/null || true
152 return 1
153 else
154 log_with_prefix "Env Loader" "Loader script syntax validated successfully"
155 fi
156
157 log_with_prefix "Env Loader" "Adding script to shell configurations"
158
159 # Add source line to both shell configs using sudo with explicit paths
160 log_with_prefix "Env Loader" "Updating .bashrc"
161 # Check if already added to avoid duplicates
162 if ! sudo grep -q "tinsnip-env-loader" "/home/$service_user/.bashrc" 2>/dev/null; then
163 if echo "source ~/.tinsnip-env-loader.sh" | sudo tee -a "/home/$service_user/.bashrc" > /dev/null 2>&1; then
164 sudo chown "$service_user:$service_user" "/home/$service_user/.bashrc"
165 log_with_prefix "Env Loader" "Added to .bashrc"
166 else
167 warn_with_prefix "Env Loader" "⚠️ Failed to update .bashrc"
168 fi
169 else
170 log_with_prefix "Env Loader" ".bashrc already configured"
171 fi
172
173 log_with_prefix "Env Loader" "Updating .profile"
174 # Check if already added to avoid duplicates
175 if ! sudo grep -q "tinsnip-env-loader" "/home/$service_user/.profile" 2>/dev/null; then
176 if echo "source ~/.tinsnip-env-loader.sh" | sudo tee -a "/home/$service_user/.profile" > /dev/null 2>&1; then
177 sudo chown "$service_user:$service_user" "/home/$service_user/.profile"
178 log_with_prefix "Env Loader" "Added to .profile"
179 else
180 warn_with_prefix "Env Loader" "⚠️ Failed to update .profile"
181 fi
182 else
183 log_with_prefix "Env Loader" ".profile already configured"
184 fi
185
186 # Test if the shell configs can be sourced without errors (with better debugging)
187 log_with_prefix "Env Loader" "Validating shell configurations"
188
189 # Check .bashrc
190 if ! sudo -u "$service_user" bash -n "/home/$service_user/.bashrc" 2>/dev/null; then
191 warn_with_prefix "Env Loader" "⚠️ Syntax error in .bashrc - checking content"
192 # Show last few lines for debugging
193 sudo tail -5 "/home/$service_user/.bashrc" | while read -r line; do
194 log_with_prefix "Debug" ".bashrc: $line"
195 done 2>/dev/null || true
196 fi
197
198 # Check .profile
199 if ! sudo -u "$service_user" bash -n "/home/$service_user/.profile" 2>/dev/null; then
200 warn_with_prefix "Env Loader" "⚠️ Syntax error in .profile - checking content"
201 # Show last few lines for debugging
202 sudo tail -5 "/home/$service_user/.profile" | while read -r line; do
203 log_with_prefix "Debug" ".profile: $line"
204 done 2>/dev/null || true
205 fi
206
207 log_with_prefix "Env Loader" "Environment loader script configured"
208}
209
210# Auto-discover NAS server for current sheet
211discover_nas_for_sheet() {
212 local sheet
213 sheet=$(get_sheet)
214
215 # Check if topsheet.station-prod registry is available
216 local registry_path="/mnt/station-prod/data/nas-credentials/nas-servers"
217 if [[ -f "$registry_path" ]]; then
218 # First check if explicitly registered for this sheet
219 local nas_server=$(grep "^${sheet}=" "$registry_path" 2>/dev/null | cut -d= -f2)
220 if [[ -n "$nas_server" ]]; then
221 echo "$nas_server"
222 return 0
223 fi
224
225 # Only fall back to default for registered sheets
226 # Check if this sheet exists in the sheet registry
227 local sheet_registry="/mnt/station-prod/data/sheets"
228 if [[ -f "$sheet_registry" ]] && grep -q "^${sheet}=" "$sheet_registry" 2>/dev/null; then
229 nas_server=$(grep "^default=" "$registry_path" 2>/dev/null | cut -d= -f2)
230 if [[ -n "$nas_server" ]]; then
231 echo "$nas_server"
232 return 0
233 fi
234 fi
235 fi
236
237 # Registry not available or no server found
238 return 1
239}
240