From 0a459f9cd0d345e0404918c634a9b8684a433071 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Tue, 25 Mar 2025 11:05:58 +0000 Subject: [PATCH] feat(cli): Add uninstall command to CLI and update shutdown delay for graceful VM shutdown --- changelog.md | 7 +++ ts/00_commitinfo_data.ts | 2 +- ts/cli.ts | 103 +++++++++++++++++++++++++++++++++++++++ ts/snmp/manager.ts | 5 +- uninstall.sh | 65 +++++++++++++++++++----- 5 files changed, 167 insertions(+), 15 deletions(-) diff --git a/changelog.md b/changelog.md index 49ef9ca..512256e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2025-03-25 - 2.1.0 - feat(cli) +Add uninstall command to CLI and update shutdown delay for graceful VM shutdown + +- Implement uninstall command in ts/cli.ts that locates and executes uninstall.sh with user prompts +- Update uninstall.sh to support environment variables for configuration and repository removal +- Increase shutdown delay in ts/snmp/manager.ts from 1 minute to 5 minutes to allow VMs more time to shut down + ## 2025-03-25 - 2.0.1 - fix(cli/systemd) Fix status command to pass debug flag and improve systemd status logging output diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 8351ac9..68bb807 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/nupst', - version: '2.0.1', + version: '2.1.0', description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices' } diff --git a/ts/cli.ts b/ts/cli.ts index a130a3d..1d20c30 100644 --- a/ts/cli.ts +++ b/ts/cli.ts @@ -1,4 +1,7 @@ import { execSync } from 'child_process'; +import { promises as fs } from 'fs'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; import { Nupst } from './nupst.js'; /** @@ -94,6 +97,10 @@ export class NupstCli { case 'update': await this.update(); break; + + case 'uninstall': + await this.uninstall(); + break; case 'help': default: @@ -366,6 +373,7 @@ Usage: nupst setup - Run the interactive setup to configure SNMP settings nupst test - Test the current configuration by connecting to the UPS nupst update - Update NUPST from repository and refresh systemd service (requires root) + nupst uninstall - Completely uninstall NUPST from the system (requires root) nupst help - Show this help message Options: @@ -835,4 +843,99 @@ Options: } } } + + /** + * Completely uninstall NUPST from the system + */ + private async uninstall(): Promise { + // Check if running as root + this.checkRootAccess('This command must be run as root.'); + + try { + // Import readline module for user input + const readline = await import('readline'); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + // Helper function to prompt for input + const prompt = (question: string): Promise => { + return new Promise((resolve) => { + rl.question(question, (answer: string) => { + resolve(answer); + }); + }); + }; + + console.log('\nNUPST Uninstaller'); + console.log('==============='); + console.log('This will completely remove NUPST from your system.\n'); + + // Ask about removing configuration + const removeConfig = await prompt('Do you want to remove the NUPST configuration files? (y/N): '); + + // Find the uninstall.sh script location + let uninstallScriptPath: string; + + // Try to determine script location based on executable path + try { + // For ESM, we can use import.meta.url, but since we might be in CJS + // we'll use a more reliable approach based on process.argv[1] + const binPath = process.argv[1]; + const modulePath = dirname(dirname(binPath)); + uninstallScriptPath = join(modulePath, 'uninstall.sh'); + + // Check if the script exists + await fs.access(uninstallScriptPath); + } catch (error) { + // If we can't find it in the expected location, try common installation paths + const commonPaths = [ + '/opt/nupst/uninstall.sh', + join(process.cwd(), 'uninstall.sh') + ]; + + for (const path of commonPaths) { + try { + await fs.access(path); + uninstallScriptPath = path; + break; + } catch { + // Continue to next path + } + } + + if (!uninstallScriptPath) { + console.error('Could not locate uninstall.sh script. Aborting uninstall.'); + rl.close(); + process.exit(1); + } + } + + // Close readline before executing script + rl.close(); + + // Execute uninstall.sh with the appropriate option + console.log(`\nRunning uninstaller from ${uninstallScriptPath}...`); + + // Pass the configuration removal option as an environment variable + const env = { + ...process.env, + REMOVE_CONFIG: removeConfig.toLowerCase() === 'y' ? 'yes' : 'no', + REMOVE_REPO: 'yes', // Always remove repo as requested + NUPST_CLI_CALL: 'true' // Flag to indicate this is being called from CLI + }; + + // Run the uninstall script with sudo + execSync(`sudo bash ${uninstallScriptPath}`, { + env, + stdio: 'inherit' // Show output in the terminal + }); + + } catch (error) { + console.error(`Uninstall failed: ${error.message}`); + process.exit(1); + } + } } \ No newline at end of file diff --git a/ts/snmp/manager.ts b/ts/snmp/manager.ts index 1fb0df3..c36535c 100644 --- a/ts/snmp/manager.ts +++ b/ts/snmp/manager.ts @@ -514,9 +514,10 @@ export class NupstSnmp { public async initiateShutdown(reason: string): Promise { console.log(`Initiating system shutdown due to: ${reason}`); try { - // Execute shutdown command - const { stdout } = await execAsync('shutdown -h +1 "UPS battery critical, shutting down in 1 minute"'); + // Execute shutdown command with 5 minute delay to allow for VM graceful shutdown + const { stdout } = await execAsync('shutdown -h +5 "UPS battery critical, shutting down in 5 minutes"'); console.log('Shutdown initiated:', stdout); + console.log('Allowing 5 minutes for VMs to shut down safely'); } catch (error) { console.error('Failed to initiate shutdown:', error); // Try a different method if first one fails diff --git a/uninstall.sh b/uninstall.sh index 7adc0a3..32b19a1 100644 --- a/uninstall.sh +++ b/uninstall.sh @@ -5,13 +5,22 @@ # Check if running as root if [ "$EUID" -ne 0 ]; then - echo "Please run as root (sudo ./uninstall.sh)" + echo "Please run as root (sudo nupst uninstall or sudo ./uninstall.sh)" exit 1 fi +# This script can be called directly or through the CLI +# When called through the CLI, environment variables are set +# REMOVE_CONFIG=yes|no - whether to remove configuration files +# REMOVE_REPO=yes|no - whether to remove the repository + +# If not set through CLI, use defaults +REMOVE_CONFIG=${REMOVE_CONFIG:-"no"} +REMOVE_REPO=${REMOVE_REPO:-"no"} + echo "NUPST Uninstaller" echo "=================" -echo "This script will completely remove NUPST from your system." +echo "This will completely remove NUPST from your system." # Find the directory where this script is located SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" @@ -37,20 +46,52 @@ if [ -L "/usr/local/bin/nupst" ]; then rm -f /usr/local/bin/nupst fi -# Step 3: Ask about removing configuration -read -p "Do you want to remove the NUPST configuration files? (y/N) " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then +# Step 3: Remove configuration if requested +if [ "$REMOVE_CONFIG" = "yes" ]; then echo "Removing configuration files..." rm -rf /etc/nupst +else + # If not called through CLI, ask user + if [ -z "$NUPST_CLI_CALL" ]; then + read -p "Do you want to remove the NUPST configuration files? (y/N) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Removing configuration files..." + rm -rf /etc/nupst + fi + fi fi -# Step 4: Check if this was a git installation -if [ -d "$SCRIPT_DIR/.git" ]; then - echo - echo "This appears to be a git installation. The local repository will remain intact." - echo "If you wish to completely remove it, you can delete the directory:" - echo " rm -rf $SCRIPT_DIR" +# Step 4: Remove repository if requested +if [ "$REMOVE_REPO" = "yes" ]; then + if [ -d "$SCRIPT_DIR/.git" ]; then + echo "Removing NUPST repository directory..." + + # Get parent directory to remove it after the script exits + PARENT_DIR=$(dirname "$SCRIPT_DIR") + REPO_NAME=$(basename "$SCRIPT_DIR") + + # Create a temporary cleanup script + CLEANUP_SCRIPT=$(mktemp) + echo "#!/bin/bash" > "$CLEANUP_SCRIPT" + echo "sleep 1" >> "$CLEANUP_SCRIPT" + echo "rm -rf \"$SCRIPT_DIR\"" >> "$CLEANUP_SCRIPT" + echo "echo \"NUPST repository has been removed.\"" >> "$CLEANUP_SCRIPT" + chmod +x "$CLEANUP_SCRIPT" + + # Run the cleanup script in the background + nohup "$CLEANUP_SCRIPT" > /dev/null 2>&1 & + + echo "NUPST repository will be removed after uninstaller exits." + else + echo "No git repository found." + fi +else + # If not requested, just display info + if [ -d "$SCRIPT_DIR/.git" ]; then + echo + echo "NUPST repository at $SCRIPT_DIR will remain intact." + fi fi # Check for npm global installation