#!/bin/bash # tin machine rm - Remove machine environment set -euo pipefail # Get tinsnip root and source libraries SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TINSNIP_ROOT="$(dirname "$(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/nas.sh" # Remove machine environment remove_machine() { local service_env="$1" local delete_data="${2:-false}" local no_confirm="${3:-false}" # Parse service-environment local parsed_output if ! parsed_output=$(parse_machine_name "$service_env" 2>/dev/null); then error_with_prefix "Machine Remove" "Invalid machine environment format: '$service_env'" echo "Expected: - (e.g., pds-dev, bsky-pds-dev)" >&2 exit 1 fi local service=$(echo "$parsed_output" | sed -n '1p') local environment=$(echo "$parsed_output" | sed -n '2p') local service_user="$service_env" log_with_prefix "Machine Remove" "Removing machine: $service_env" echo # Check if user exists if ! id "$service_user" &>/dev/null 2>&1; then warn_with_prefix "Machine Remove" "User '$service_user' does not exist" echo "Machine may have already been removed or never created." >&2 exit 0 fi local service_uid=$(id -u "$service_user") local mount_point="/mnt/$service_env" echo "Machine Details:" echo " User: $service_user" echo " UID: $service_uid" echo " Mount: $mount_point" echo # Check for deployed services local services=() if [[ -d "$mount_point/service" ]]; then while IFS= read -r -d '' service_dir; do services+=("$(basename "$service_dir")") done < <(find "$mount_point/service" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null) fi if [[ ${#services[@]} -gt 0 ]]; then echo "WARNING: Found ${#services[@]} deployed service(s):" for svc in "${services[@]}"; do echo " - $svc" done echo if [[ "$no_confirm" == "true" ]]; then # Auto-confirm when called from sheet removal log_with_prefix "Machine Remove" "Removing all services..." else read -p "Remove all services first? [Y/n]: " remove_services case "${remove_services:-y}" in [Yy]*|"") ;; *) error_with_prefix "Machine Remove" "Cannot remove machine with deployed services" echo "Remove services first with: tin service rm $service_env " >&2 exit 1 ;; esac fi for svc in "${services[@]}"; do log_with_prefix "Machine Remove" "Removing service: $svc" "$TINSNIP_ROOT/cmd/service/rm.sh" "$service_env" "$svc" <<< "y" done echo fi # Show data policy if [[ "$delete_data" == "true" ]]; then echo "WARNING: NAS data will be DELETED" else echo "SUCCESS: NAS data will be PRESERVED" fi echo if [[ "$no_confirm" == "true" ]]; then # Auto-confirm when called from sheet removal log_with_prefix "Machine Remove" "Proceeding with removal..." else read -p "Remove this machine? [y/N]: " confirm case "${confirm}" in [Yy]) log_with_prefix "Machine Remove" "Proceeding with removal..." ;; *) log_with_prefix "Machine Remove" "Removal cancelled" exit 0 ;; esac fi # Delete NAS data if requested if [[ "$delete_data" == "true" ]]; then log_with_prefix "Machine Remove" "Deleting NAS data..." # Find NAS server from fstab local nas_path=$(grep "$mount_point" /etc/fstab 2>/dev/null | awk '{print $1}' | head -1) if [[ -n "$nas_path" ]]; then echo " NAS path: $nas_path" local nas_server=$(echo "$nas_path" | cut -d: -f1) local nas_dir=$(echo "$nas_path" | cut -d: -f2) echo " Deleting data on NAS: $nas_server" echo " This will require sudo password on the NAS..." echo "" # Use interactive SSH with -t flag to enable password prompt # This also removes the NFS export and reloads if ssh -t "$nas_server" "echo 'Removing NAS data and export...' && \ sudo rm -rf '$nas_dir' && \ echo 'Data deleted: $nas_dir' && \ sudo sed -i '\|$nas_dir|d' /etc/exports && \ echo 'Export removed from /etc/exports' && \ sudo exportfs -ra && \ echo 'NFS exports reloaded'"; then echo "" log_with_prefix "Machine Remove" "✅ NAS data and export deleted successfully" else echo "" warn_with_prefix "Machine Remove" "Failed to delete NAS data" echo " Manually delete with:" >&2 echo " ssh $nas_server" >&2 echo " sudo rm -rf '$nas_dir'" >&2 echo " sudo sed -i '\|$nas_dir|d' /etc/exports" >&2 echo " sudo exportfs -ra" >&2 fi else warn_with_prefix "Machine Remove" "Could not find NAS path in /etc/fstab" echo " NFS mount information not available for cleanup" >&2 fi else log_with_prefix "Machine Remove" "Preserving NAS data (use --delete-data to remove)" fi # Unmount NFS log_with_prefix "Machine Remove" "Unmounting NFS..." if mountpoint -q "$mount_point" 2>/dev/null; then sudo umount "$mount_point" 2>/dev/null || sudo umount -l "$mount_point" fi # Remove from fstab log_with_prefix "Machine Remove" "Removing from /etc/fstab..." sudo sed -i "\|$mount_point|d" /etc/fstab # Remove user log_with_prefix "Machine Remove" "Removing user $service_user..." # Kill any processes owned by the user first if id "$service_user" &>/dev/null; then sudo pkill -9 -u "$service_user" 2>/dev/null || true sleep 1 # Give processes time to terminate fi # Remove user and home directory if sudo userdel -r "$service_user" 2>/dev/null; then log_with_prefix "Machine Remove" "User removed successfully" else warn_with_prefix "Machine Remove" "Failed to remove user (may need manual cleanup)" fi # Remove mount point directory if [[ -d "$mount_point" ]]; then sudo rmdir "$mount_point" 2>/dev/null || sudo rm -rf "$mount_point" fi # Remove service from registry log_with_prefix "Machine Remove" "Removing from machine registry..." # Detect sheet from UID local sheet=$(get_sheet_from_uid "$service_uid") log_with_prefix "Machine Remove" "Detected sheet: $sheet (from UID $service_uid)" if unregister_service "$service" "$sheet" 2>/dev/null; then log_with_prefix "Machine Remove" "Service unregistered from $sheet registry" else warn_with_prefix "Machine Remove" "Service was not in registry for sheet $sheet" fi echo log_with_prefix "Machine Remove" "Machine '$service_env' removed successfully" if [[ "$delete_data" != "true" ]]; then echo log_with_prefix "Machine Remove" "NAS data preserved. To restore:" log_with_prefix "Machine Remove" " tin machine create $service $environment " log_with_prefix "Machine Remove" " tin service deploy $service_env " fi } show_help() { cat << EOF tin machine rm - Remove machine environment USAGE: tin machine rm [--delete-data] [--no-confirm] DESCRIPTION: Remove a machine environment including user and NFS mount. By default, NAS data is PRESERVED for recovery. Automatically removes all deployed services first. ARGUMENTS: Machine to remove (e.g., pds-dev) OPTIONS: --delete-data Also delete data on NAS (DANGEROUS!) --no-confirm Skip confirmation prompts (for automation) EXAMPLES: tin machine rm pds-dev # Remove machine, keep NAS data tin machine rm pds-dev --delete-data # Remove machine AND delete NAS data tin machine rm pds-dev --no-confirm # Remove without prompts NOTES: - Automatically removes deployed services (with confirmation unless --no-confirm) - Default behavior preserves NAS data (CD-friendly) - Machine can be recreated and data remounted for recovery - Use --delete-data only when permanently decommissioning - NAS deletion requires sudo password (interactive prompt) - Also removes NFS export from /etc/exports and reloads - --no-confirm keeps stdin as terminal for interactive NAS password WARNING: --delete-data will PERMANENTLY DELETE all data on the NAS! You will be prompted for the NAS sudo password during deletion. EOF } # Handle help flags case "${1:-}" in --help|-h|help) show_help exit 0 ;; esac # Main execution if [[ $# -eq 0 ]]; then error_with_prefix "Machine Remove" "Service environment required" echo "Usage: tin machine rm [--delete-data] [--no-confirm]" >&2 exit 1 fi service_env="$1" delete_data="false" no_confirm="false" # Parse flags shift while [[ $# -gt 0 ]]; do case "$1" in --delete-data) delete_data="true" ;; --no-confirm) no_confirm="true" ;; *) error_with_prefix "Machine Remove" "Unknown flag: $1" echo "Usage: tin machine rm [--delete-data] [--no-confirm]" >&2 exit 1 ;; esac shift done remove_machine "$service_env" "$delete_data" "$no_confirm"