From 30c89de134863df118b9cbb6ebd57027b772f6ce Mon Sep 17 00:00:00 2001 From: Muhammad Ibrahim Date: Sat, 18 Oct 2025 22:59:03 +0100 Subject: [PATCH] Improved detection logic and upgrade mechanism using intermeditary script --- agents/patchmon-agent.sh | 319 +++++-------------------------- backend/src/routes/hostRoutes.js | 71 +------ 2 files changed, 47 insertions(+), 343 deletions(-) diff --git a/agents/patchmon-agent.sh b/agents/patchmon-agent.sh index 4a981be..f2b47e0 100755 --- a/agents/patchmon-agent.sh +++ b/agents/patchmon-agent.sh @@ -80,171 +80,6 @@ load_legacy_credentials() { fi } -# Convert legacy credentials to YAML format -convert_credentials_to_yaml() { - local yaml_file="/etc/patchmon/credentials.yml" - - info "Converting credentials to YAML format..." - - cat > "$yaml_file" << EOF -api_id: "$API_ID" -api_key: "$API_KEY" -EOF - - chmod 600 "$yaml_file" - success "Credentials converted to YAML format" -} - -# Create Go agent configuration -create_go_agent_config() { - local config_file="/etc/patchmon/config.yml" - - info "Creating Go agent configuration..." - - cat > "$config_file" << EOF -patchmon_server: "$PATCHMON_SERVER" -api_version: "$API_VERSION" -credentials_file: "/etc/patchmon/credentials.yml" -log_file: "/etc/patchmon/logs/patchmon-agent.log" -log_level: "info" -EOF - - chmod 644 "$config_file" - success "Go agent configuration created" -} - -# Download Go agent binary -download_go_agent() { - local arch=$(uname -m) - local goos="linux" - local goarch="" - - # Map architecture - case "$arch" in - "x86_64") - goarch="amd64" - ;; - "i386"|"i686") - goarch="386" - ;; - "aarch64"|"arm64") - goarch="arm64" - ;; - "armv7l"|"armv6l"|"armv5l") - goarch="arm" - ;; - *) - error "Unsupported architecture: $arch" - ;; - esac - - local download_url="$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/go-binary?arch=$goarch" - local temp_dir="/etc/patchmon/tmp" - local temp_binary="$temp_dir/patchmon-agent-new" - - # Create temp directory if it doesn't exist - mkdir -p "$temp_dir" - - info "Downloading Go agent binary for $goos-$goarch..." - - # Download with API credentials - if curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" \ - -o "$temp_binary" "$download_url"; then - - # Verify binary - check if it's a valid executable - chmod +x "$temp_binary" - - # Check if file exists and is executable - if [[ -f "$temp_binary" ]] && [[ -x "$temp_binary" ]]; then - success "Go agent binary downloaded successfully" - echo "$temp_binary" - else - # Try to get more info about the file - local file_output=$(file "$temp_binary" 2>/dev/null || echo "unknown") - rm -f "$temp_binary" # Clean up failed download - error "Downloaded file is not executable. File info: $file_output" - fi - else - rm -f "$temp_binary" # Clean up failed download - error "Failed to download Go agent binary" - fi -} - -# Install Go agent binary and service -install_go_agent() { - local temp_binary="$1" - local install_path="/usr/local/bin/patchmon-agent" - - # Check if temp_binary is provided and exists - if [[ -z "$temp_binary" ]] || [[ ! -f "$temp_binary" ]]; then - error "No valid binary provided for installation" - fi - - info "Installing Go agent binary..." - - # Create backup of current script if it exists - if [[ -f "/usr/local/bin/patchmon-agent.sh" ]]; then - local backup_path="/usr/local/bin/patchmon-agent.sh.backup.$(date +%Y%m%d_%H%M%S)" - cp "/usr/local/bin/patchmon-agent.sh" "$backup_path" - info "Backed up legacy script to: $backup_path" - fi - - # Install new binary - mv "$temp_binary" "$install_path" - success "Go agent binary installed to: $install_path" - - # Clean up the temporary file (it's now moved, so this is just safety) - rm -f "$temp_binary" - - # Install and start systemd service - install_systemd_service -} - -# Install and start systemd service -install_systemd_service() { - info "Installing systemd service..." - - # Create systemd service file - cat > /etc/systemd/system/patchmon-agent.service << EOF -[Unit] -Description=PatchMon Agent -After=network.target - -[Service] -Type=simple -User=root -ExecStart=/usr/local/bin/patchmon-agent serve -Restart=always -RestartSec=5 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target -EOF - - # Reload systemd and enable service - systemctl daemon-reload - systemctl enable patchmon-agent.service - - # Start the service - info "Starting PatchMon agent service..." - if systemctl start patchmon-agent.service; then - success "PatchMon agent service started successfully" - - # Wait a moment for service to start - sleep 3 - - # Check if service is running - if systemctl is-active --quiet patchmon-agent.service; then - success "PatchMon agent service is running" - else - warning "PatchMon agent service failed to start" - fi - else - warning "Failed to start PatchMon agent service" - fi -} # Remove cron entries remove_cron_entries() { @@ -274,74 +109,6 @@ remove_cron_entries() { fi } -# Configure Go agent -configure_go_agent() { - info "Configuring Go agent..." - - # Create necessary directories - mkdir -p /etc/patchmon/logs - - # Check if the Go agent binary exists - if [[ ! -f "/usr/local/bin/patchmon-agent" ]]; then - warning "Go agent binary not found at /usr/local/bin/patchmon-agent" - warning "Configuration files were created manually" - return 0 - fi - - # Configure credentials - if ! /usr/local/bin/patchmon-agent config set-credentials "$API_ID" "$API_KEY"; then - warning "Failed to configure credentials via CLI, but files were created manually" - fi - - success "Go agent configured" -} - -# Test Go agent -test_go_agent() { - info "Testing Go agent..." - - # Check if the Go agent binary exists - if [[ ! -f "/usr/local/bin/patchmon-agent" ]]; then - warning "Go agent binary not found - skipping tests" - return 0 - fi - - # Wait a bit for service to fully start - sleep 2 - - # Test service status - if systemctl is-active --quiet patchmon-agent.service; then - success "PatchMon agent service is running" - else - warning "PatchMon agent service is not running" - fi - - # Test configuration - if /usr/local/bin/patchmon-agent config show >/dev/null 2>&1; then - success "Go agent configuration test passed" - else - warning "Go agent configuration test failed, but continuing..." - fi - - # Test connectivity (ping test) - info "Testing connectivity to PatchMon server..." - if /usr/local/bin/patchmon-agent ping >/dev/null 2>&1; then - success "Go agent connectivity test passed - server is reachable" - else - warning "Go agent connectivity test failed - server may be unreachable" - fi -} - -# Clean up temporary directory -cleanup_temp_directory() { - info "Cleaning up temporary files..." - - local temp_dir="/etc/patchmon/tmp" - if [[ -d "$temp_dir" ]]; then - rm -rf "$temp_dir" - success "Temporary directory cleaned up" - fi -} # Clean up legacy files (only after successful verification) cleanup_legacy_files() { @@ -376,11 +143,10 @@ show_migration_summary() { echo "✅ Successfully migrated from bash agent to Go agent" echo "" echo "What was done:" - echo " • Converted credentials to YAML format" - echo " • Created Go agent configuration" - echo " • Downloaded and installed Go agent binary" - echo " • Installed and started systemd service" - echo " • Verified service is running and connected" + echo " • Loaded legacy credentials" + echo " • Downloaded and ran PatchMon install script" + echo " • Installed Go agent binary and systemd service" + echo " • Verified service is running" echo " • Removed legacy cron entries" echo " • Cleaned up legacy files" echo "" @@ -466,50 +232,53 @@ perform_migration() { # Load legacy credentials load_legacy_credentials - # Convert credentials - convert_credentials_to_yaml + # Set environment variables for install script + export API_ID="$API_ID" + export API_KEY="$API_KEY" + export PATCHMON_URL="$PATCHMON_SERVER" - # Create Go agent config - create_go_agent_config + # Detect architecture + local arch=$(uname -m) + local goarch="" + case "$arch" in + "x86_64") goarch="amd64" ;; + "i386"|"i686") goarch="386" ;; + "aarch64"|"arm64") goarch="arm64" ;; + "armv7l"|"armv6l"|"armv5l") goarch="arm" ;; + *) error "Unsupported architecture: $arch" ;; + esac - # Download Go agent - local temp_binary=$(download_go_agent) + info "Downloading and running PatchMon install script..." - # Install Go agent binary and service - install_go_agent "$temp_binary" - - # Configure Go agent - configure_go_agent - - # Test Go agent (including service and connectivity) - test_go_agent - - # Only proceed with cleanup if tests pass - if systemctl is-active --quiet patchmon-agent.service && /usr/local/bin/patchmon-agent ping >/dev/null 2>&1; then - success "Go agent is running and connected - proceeding with cleanup" + # Download and run the install script + if curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" \ + "$PATCHMON_SERVER/api/v1/hosts/install?arch=$goarch" | bash; then - # Remove cron entries - remove_cron_entries + success "PatchMon Go agent installed successfully" - # Clean up legacy files - cleanup_legacy_files + # Wait a moment for service to start + sleep 3 - # Clean up temporary directory - cleanup_temp_directory - - # Show summary - show_migration_summary - - # Run post-migration verification - post_migration_check - - success "Migration completed successfully!" + # Test if the service is running + if systemctl is-active --quiet patchmon-agent.service; then + success "PatchMon agent service is running" + + # Clean up legacy files + remove_cron_entries + cleanup_legacy_files + + # Show summary + show_migration_summary + post_migration_check + + success "Migration completed successfully!" + else + warning "PatchMon agent service failed to start" + warning "Legacy files preserved for debugging" + show_migration_summary + fi else - warning "Go agent verification failed - skipping cleanup to preserve legacy files" - warning "You may need to manually clean up legacy files after fixing the Go agent" - - # Show summary anyway - show_migration_summary + error "Failed to install PatchMon Go agent" fi # Exit here to prevent the legacy script from continuing diff --git a/backend/src/routes/hostRoutes.js b/backend/src/routes/hostRoutes.js index 34e9417..b67e850 100644 --- a/backend/src/routes/hostRoutes.js +++ b/backend/src/routes/hostRoutes.js @@ -38,10 +38,11 @@ router.get("/agent/download", async (req, res) => { const path = require("node:path"); // Check if this is a legacy agent (bash script) requesting update - // Legacy agents will have agent_version < 1.3.0 + // Legacy agents will have agent_version < 1.2.9 (excluding 1.2.9 itself) const isLegacyAgent = host.agent_version && - (host.agent_version.startsWith("1.2.") || + ((host.agent_version.startsWith("1.2.") && + host.agent_version !== "1.2.9") || host.agent_version.startsWith("1.1.") || host.agent_version.startsWith("1.0.")); @@ -118,72 +119,6 @@ router.get("/agent/download", async (req, res) => { } }); -// Endpoint to download Go agent binary (for migration script) -router.get("/agent/go-binary", async (req, res) => { - try { - // Verify API credentials - const apiId = req.headers["x-api-id"]; - const apiKey = req.headers["x-api-key"]; - - if (!apiId || !apiKey) { - return res.status(401).json({ error: "API credentials required" }); - } - - // Validate API credentials - const host = await prisma.hosts.findUnique({ - where: { api_id: apiId }, - }); - - if (!host || host.api_key !== apiKey) { - return res.status(401).json({ error: "Invalid API credentials" }); - } - - const fs = require("node:fs"); - const path = require("node:path"); - - // Get architecture parameter (default to amd64) - const architecture = req.query.arch || "amd64"; - - // Validate architecture - const validArchitectures = ["amd64", "386", "arm64", "arm"]; - if (!validArchitectures.includes(architecture)) { - return res.status(400).json({ - error: "Invalid architecture. Must be one of: amd64, 386, arm64, arm", - }); - } - - const binaryName = `patchmon-agent-linux-${architecture}`; - const binaryPath = path.join(__dirname, "../../../agents", binaryName); - - if (!fs.existsSync(binaryPath)) { - return res.status(404).json({ - error: `Agent binary not found for architecture: ${architecture}`, - }); - } - - // Set appropriate headers for binary download - res.setHeader("Content-Type", "application/octet-stream"); - res.setHeader( - "Content-Disposition", - `attachment; filename="${binaryName}"`, - ); - - // Stream the binary file - const fileStream = fs.createReadStream(binaryPath); - fileStream.pipe(res); - - fileStream.on("error", (error) => { - console.error("Binary stream error:", error); - if (!res.headersSent) { - res.status(500).json({ error: "Failed to stream agent binary" }); - } - }); - } catch (error) { - console.error("Go binary download error:", error); - res.status(500).json({ error: "Failed to serve Go agent binary" }); - } -}); - // Version check endpoint for agents router.get("/agent/version", async (_req, res) => { try {