#!/bin/bash # Setup sheet station - the infrastructure machine for a sheet # Handles machine registry and shared configuration set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TINSNIP_ROOT="$(dirname "$SCRIPT_DIR")" source "$TINSNIP_ROOT/lib/core.sh" source "$TINSNIP_ROOT/lib/uid.sh" source "$TINSNIP_ROOT/lib/registry.sh" source "$TINSNIP_ROOT/lib/nfs.sh" # Parameters NAS_SERVER="${1:-}" TIN_SHEET="${TIN_SHEET:-topsheet}" # Setup logging functions using shared lib log() { log_with_prefix "Station Setup" "$@" } error() { error_with_prefix "Station Setup" "$@" } warn() { warn_with_prefix "Station Setup" "$@" } # Discover existing machines by scanning system UIDs (SMEP scheme) discover_existing_machines() { local sheet_num=$(get_sheet_number "$TIN_SHEET") local machines=() log "Discovering existing machines in sheet '$TIN_SHEET'..." >&2 # Scan for users matching SMEP UID pattern # SMEP Pattern: ${sheet_num}${machine_num_2digit}${env_num}${port} for machine_num in {01..99}; do for env_num in {0..9}; do local smep_uid="${sheet_num}${machine_num}${env_num}0" if getent passwd "$smep_uid" >/dev/null 2>&1; then local username=$(getent passwd "$smep_uid" | cut -d: -f1) local env_name # Map environment numbers to names (expanded mapping) case "$env_num" in 0) env_name="prod" ;; 1) env_name="test" ;; 2) env_name="dev" ;; 3) env_name="staging" ;; 4) env_name="demo" ;; 5) env_name="qa" ;; 6) env_name="uat" ;; 7) env_name="preview" ;; 8) env_name="canary" ;; 9) env_name="local" ;; esac # Extract machine name from username pattern: machine-env if [[ "$username" =~ ^([^-]+)-${env_name}$ ]]; then local machine_name="${BASH_REMATCH[1]}" log "Found: $machine_name ($env_name) - UID $smep_uid (user: $username)" >&2 machines+=("$machine_name=$machine_num") else log "Found UID $smep_uid but couldn't parse machine name from username: $username" >&2 fi fi done done # Return unique machine mappings printf '%s\n' "${machines[@]}" | sort -u } # Create machine registry from discovered machines create_machine_registry() { local mount_point="/mnt/station-prod" local sheet="${TIN_SHEET:-topsheet}" # Create machines directory for per-sheet registries sudo -u station-prod mkdir -p "$mount_point/data/machines" # Create backward compatibility symlink (services → machines) if [[ ! -e "$mount_point/data/services" ]]; then log "Creating backward compatibility symlink: services → machines" sudo -u station-prod ln -s machines "$mount_point/data/services" fi local registry_file="$mount_point/data/machines/$sheet/registry" # Create sheet directory if needed sudo -u station-prod mkdir -p "$mount_point/data/machines/$sheet" # Only create registry if it doesn't exist (preserve existing registrations) if [[ ! -f "$registry_file" ]]; then log "Creating new machine registry for sheet '$sheet'" # Discover machines local machines=$(discover_existing_machines) # Create per-sheet registry file with only station (other machines register themselves) cat << EOF | sudo -u station-prod tee "$registry_file" > /dev/null # Machine Registry for $sheet sheet # Format: machine_name=machine_number (2-digit format) # Machine 00 is reserved for station (sheet infrastructure) station=00 EOF # Add discovered machines if [[ -n "$machines" ]]; then echo "$machines" | sudo -u station-prod tee -a "$registry_file" > /dev/null fi log "Machine registry created for sheet '$sheet' at $registry_file with $(wc -l < "$registry_file") entries" else log "Machine registry already exists for sheet '$sheet', preserving existing entries" fi } # Create global sheet registry (only for topsheet sheet) create_sheet_registry() { local mount_point="/mnt/station-prod" local sheet_registry="$mount_point/data/sheets" # Only create sheet registry for topsheet sheet if [[ "$TIN_SHEET" != "topsheet" ]]; then return 0 fi log "Creating global sheet registry..." # Create data directory sudo -u station-prod mkdir -p "$mount_point/data" # Create sheet registry file with a topsheet entry cat << EOF | sudo -u station-prod tee "$sheet_registry" > /dev/null # Global Sheet Registry # Format: sheet_name=sheet_number # This registry prevents sheet number collisions # Only tinsnip.station-prod maintains this registry topsheet=5 EOF log "Global sheet registry created: $sheet_registry" } # Create NAS credentials storage (only for topsheet sheet) setup_nas_credentials() { local mount_point="/mnt/station-prod" local nas_creds_dir="$mount_point/data/nas-credentials" # Only create NAS credentials storage for topsheet sheet if [[ "$TIN_SHEET" != "topsheet" ]]; then return 0 fi log "Setting up NAS credentials storage..." # Create credentials directory with restricted permissions sudo -u station-prod mkdir -p "$nas_creds_dir" sudo -u station-prod chmod 700 "$nas_creds_dir" # Create placeholder files sudo -u station-prod touch "$nas_creds_dir/README.md" cat << EOF | sudo -u station-prod tee "$nas_creds_dir/README.md" > /dev/null # NAS Credentials Storage This directory stores SSH keys and credentials for NAS access. Structure: - \`.key\` - Private SSH keys for each NAS server - \`authorized_keys\` - Public keys for distribution to NAS servers - \`nas-servers\` - Registry of NAS servers per sheet Security: - Directory permissions: 700 (station-prod user only) - Key file permissions: 600 (when added) EOF # Create NAS server registry with the server used for bootstrap if [[ -n "$NAS_SERVER" ]]; then local nas_registry="$nas_creds_dir/nas-servers" cat << EOF | sudo -u station-prod tee "$nas_registry" > /dev/null # NAS Server Registry # Format: sheet=nas_server # This tracks which NAS servers are used by each sheet ${TIN_SHEET}=$NAS_SERVER EOF log "NAS server registry created: $NAS_SERVER registered" fi log "NAS credentials storage ready: $nas_creds_dir" } # Deploy catalog and scripts to station deploy_station_artifacts() { local mount_point="/mnt/station-prod" local tinsnip_root="$HOME/.local/opt/$TIN_SHEET.tinsnip" log "Deploying station artifacts..." # Create directory structure for scripts sudo -u station-prod mkdir -p "$mount_point/opt/scripts" # Note: Service catalog is maintained in separate repository # at ~/.local/opt/{sheet}.service/ (e.g., dynamicalsystem.service) log "Station artifacts deployed" } # Setup station user and mount using library functions setup_station_mount() { local service_name="station" local service_env="prod" local service_uid="$(calculate_service_uid "$service_name" "$service_env")" local service_user="${service_name}-${service_env}" # Create service user if ! create_service_user "$service_user" "$service_uid"; then error "Failed to create service user $service_user" fi # Setup NFS mount if ! setup_nfs_mount "$service_name" "$service_env" "$service_uid" "$NAS_SERVER" "$TIN_SHEET"; then error "Failed to setup station NFS mount" fi # Setup XDG integration if ! setup_xdg_integration "$service_name" "$service_env" "$TIN_SHEET"; then error "Failed to setup XDG integration for station" fi } # Main setup flow main() { if [[ -z "$NAS_SERVER" ]]; then echo "Usage: $0 " echo "" echo "Sets up the sheet station for machine registry and shared config" exit 1 fi local station_uid=$(calculate_service_uid "station" "prod") log "Setting up station:" log " Sheet: $TIN_SHEET" log " NFS mount: /mnt/station-prod" log " User: station-prod (UID: $station_uid)" # Setup station mount and user (mount_nas.sh handles existence check and creation guidance) setup_station_mount # Create registries create_machine_registry create_sheet_registry # Only for topsheet sheet setup_nas_credentials # Only for topsheet sheet # Deploy catalog and scripts deploy_station_artifacts log "Station setup complete!" if [[ "$TIN_SHEET" == "topsheet" ]]; then log " Global sheet registry: /mnt/station-prod/data/sheets" log " NAS credentials: /mnt/station-prod/data/nas-credentials/" fi log " Machine registry: /mnt/station-prod/data/machines" log " Scripts: /mnt/station-prod/opt/scripts/" } main "$@"