Improved detection logic and upgrade mechanism using intermeditary script

This commit is contained in:
Muhammad Ibrahim
2025-10-18 22:59:03 +01:00
parent 4b35fc9ab9
commit 30c89de134
2 changed files with 47 additions and 343 deletions

View File

@@ -80,171 +80,6 @@ load_legacy_credentials() {
fi 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
remove_cron_entries() { remove_cron_entries() {
@@ -274,74 +109,6 @@ remove_cron_entries() {
fi 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) # Clean up legacy files (only after successful verification)
cleanup_legacy_files() { cleanup_legacy_files() {
@@ -376,11 +143,10 @@ show_migration_summary() {
echo "✅ Successfully migrated from bash agent to Go agent" echo "✅ Successfully migrated from bash agent to Go agent"
echo "" echo ""
echo "What was done:" echo "What was done:"
echo " • Converted credentials to YAML format" echo " • Loaded legacy credentials"
echo " • Created Go agent configuration" echo " • Downloaded and ran PatchMon install script"
echo " • Downloaded and installed Go agent binary" echo " • Installed Go agent binary and systemd service"
echo " • Installed and started systemd service" echo " • Verified service is running"
echo " • Verified service is running and connected"
echo " • Removed legacy cron entries" echo " • Removed legacy cron entries"
echo " • Cleaned up legacy files" echo " • Cleaned up legacy files"
echo "" echo ""
@@ -466,50 +232,53 @@ perform_migration() {
# Load legacy credentials # Load legacy credentials
load_legacy_credentials load_legacy_credentials
# Convert credentials # Set environment variables for install script
convert_credentials_to_yaml export API_ID="$API_ID"
export API_KEY="$API_KEY"
export PATCHMON_URL="$PATCHMON_SERVER"
# Create Go agent config # Detect architecture
create_go_agent_config 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 info "Downloading and running PatchMon install script..."
local temp_binary=$(download_go_agent)
# Install Go agent binary and service # Download and run the install script
install_go_agent "$temp_binary" 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
# 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"
# Remove cron entries success "PatchMon Go agent installed successfully"
remove_cron_entries
# Clean up legacy files # Wait a moment for service to start
cleanup_legacy_files sleep 3
# Clean up temporary directory # Test if the service is running
cleanup_temp_directory if systemctl is-active --quiet patchmon-agent.service; then
success "PatchMon agent service is running"
# Show summary
show_migration_summary # Clean up legacy files
remove_cron_entries
# Run post-migration verification cleanup_legacy_files
post_migration_check
# Show summary
success "Migration completed successfully!" 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 else
warning "Go agent verification failed - skipping cleanup to preserve legacy files" error "Failed to install PatchMon Go agent"
warning "You may need to manually clean up legacy files after fixing the Go agent"
# Show summary anyway
show_migration_summary
fi fi
# Exit here to prevent the legacy script from continuing # Exit here to prevent the legacy script from continuing

View File

@@ -38,10 +38,11 @@ router.get("/agent/download", async (req, res) => {
const path = require("node:path"); const path = require("node:path");
// Check if this is a legacy agent (bash script) requesting update // 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 = const isLegacyAgent =
host.agent_version && 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.1.") ||
host.agent_version.startsWith("1.0.")); 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 // Version check endpoint for agents
router.get("/agent/version", async (_req, res) => { router.get("/agent/version", async (_req, res) => {
try { try {