mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-15 09:49:48 +00:00
Windows Host Agent Enhancements: - Implement native Windows service support using golang.org/x/sys/windows/svc - Add Windows Event Log integration for troubleshooting - Create professional PowerShell installation/uninstallation scripts - Add process termination and retry logic to handle Windows file locking - Register uninstall endpoint at /uninstall-host-agent.ps1 Host Agent UI Improvements: - Add expandable drawer to Hosts page (click row to view details) - Display system info, network interfaces, disks, and temperatures in cards - Replace status badges with subtle colored indicators - Remove redundant master-detail sidebar layout - Add search filtering for hosts Technical Details: - service_windows.go: Windows service lifecycle management with graceful shutdown - service_stub.go: Cross-platform compatibility for non-Windows builds - install-host-agent.ps1: Full Windows installation with validation - uninstall-host-agent.ps1: Clean removal with process termination and retries - HostsOverview.tsx: Expandable row pattern matching Docker/Proxmox pages Files Added: - cmd/pulse-host-agent/service_windows.go - cmd/pulse-host-agent/service_stub.go - scripts/install-host-agent.ps1 - scripts/uninstall-host-agent.ps1 - frontend-modern/src/components/Hosts/HostsOverview.tsx - frontend-modern/src/components/Hosts/HostsFilter.tsx The Windows service now starts reliably with automatic restart on failure, and the uninstall script handles file locking gracefully without requiring reboots.
228 lines
8.5 KiB
PowerShell
228 lines
8.5 KiB
PowerShell
# Pulse Host Agent Installation Script for Windows
|
||
#
|
||
# Usage:
|
||
# iwr -useb http://pulse-server:7656/install-host-agent.ps1 | iex
|
||
# OR with parameters:
|
||
# $url = "http://pulse-server:7656"; $token = "your-token"; iwr -useb "$url/install-host-agent.ps1" | iex
|
||
#
|
||
# Parameters can be passed via environment variables or script parameters
|
||
|
||
param(
|
||
[string]$PulseUrl = $env:PULSE_URL,
|
||
[string]$Token = $env:PULSE_TOKEN,
|
||
[string]$Interval = $env:PULSE_INTERVAL,
|
||
[string]$InstallPath = "C:\Program Files\Pulse",
|
||
[switch]$NoService
|
||
)
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
|
||
# ANSI color codes for output
|
||
$Red = "`e[31m"
|
||
$Green = "`e[32m"
|
||
$Yellow = "`e[33m"
|
||
$Blue = "`e[34m"
|
||
$Reset = "`e[0m"
|
||
|
||
function Write-Color {
|
||
param([string]$Color, [string]$Message)
|
||
Write-Host "${Color}${Message}${Reset}"
|
||
}
|
||
|
||
function Write-Success { param([string]$msg) Write-Color $Green "✓ $msg" }
|
||
function Write-Error { param([string]$msg) Write-Color $Red "✗ $msg" }
|
||
function Write-Info { param([string]$msg) Write-Color $Blue "ℹ $msg" }
|
||
function Write-Warning { param([string]$msg) Write-Color $Yellow "⚠ $msg" }
|
||
|
||
Write-Host ""
|
||
Write-Color $Blue "═══════════════════════════════════════════════════════════"
|
||
Write-Color $Blue " Pulse Host Agent - Windows Installation"
|
||
Write-Color $Blue "═══════════════════════════════════════════════════════════"
|
||
Write-Host ""
|
||
|
||
# Check if running as Administrator
|
||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||
if (-not $isAdmin) {
|
||
Write-Error "This script must be run as Administrator"
|
||
Write-Info "Right-click PowerShell and select 'Run as Administrator'"
|
||
exit 1
|
||
}
|
||
|
||
# Interactive prompts if parameters not provided
|
||
if (-not $PulseUrl) {
|
||
$PulseUrl = Read-Host "Enter Pulse server URL (e.g., http://pulse.example.com:7656)"
|
||
}
|
||
$PulseUrl = $PulseUrl.TrimEnd('/')
|
||
|
||
if (-not $Token) {
|
||
Write-Warning "No API token provided - agent will attempt to connect without authentication"
|
||
$response = Read-Host "Continue without token? (y/N)"
|
||
if ($response -ne 'y' -and $response -ne 'Y') {
|
||
$Token = Read-Host "Enter API token"
|
||
}
|
||
}
|
||
|
||
if (-not $Interval) {
|
||
$Interval = "30s"
|
||
}
|
||
|
||
Write-Info "Configuration:"
|
||
Write-Host " Pulse URL: $PulseUrl"
|
||
Write-Host " Token: $(if ($Token) { '***' + $Token.Substring([Math]::Max(0, $Token.Length - 4)) } else { 'none' })"
|
||
Write-Host " Interval: $Interval"
|
||
Write-Host " Install Path: $InstallPath"
|
||
Write-Host ""
|
||
|
||
# Determine architecture
|
||
$arch = if ([Environment]::Is64BitOperatingSystem) { "amd64" } else { "386" }
|
||
$downloadUrl = "$PulseUrl/download/pulse-host-agent?platform=windows&arch=$arch"
|
||
|
||
Write-Info "Downloading agent binary from $downloadUrl..."
|
||
try {
|
||
# Create install directory
|
||
if (-not (Test-Path $InstallPath)) {
|
||
New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null
|
||
}
|
||
|
||
$agentPath = Join-Path $InstallPath "pulse-host-agent.exe"
|
||
|
||
# Download binary
|
||
Invoke-WebRequest -Uri $downloadUrl -OutFile $agentPath -UseBasicParsing
|
||
Write-Success "Downloaded agent to $agentPath"
|
||
} catch {
|
||
Write-Error "Failed to download agent: $_"
|
||
exit 1
|
||
}
|
||
|
||
# Create configuration
|
||
$configPath = Join-Path $InstallPath "config.json"
|
||
$config = @{
|
||
url = $PulseUrl
|
||
interval = $Interval
|
||
}
|
||
if ($Token) {
|
||
$config.token = $Token
|
||
}
|
||
|
||
$config | ConvertTo-Json | Set-Content $configPath
|
||
Write-Success "Created configuration at $configPath"
|
||
|
||
# Stop existing service if running
|
||
$serviceName = "PulseHostAgent"
|
||
$existingService = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
|
||
if ($existingService) {
|
||
Write-Info "Stopping existing service..."
|
||
Stop-Service -Name $serviceName -Force
|
||
Write-Success "Stopped existing service"
|
||
}
|
||
|
||
if (-not $NoService) {
|
||
Write-Info "Installing native Windows service with built-in service support..."
|
||
|
||
# Build service arguments
|
||
$serviceArgs = @(
|
||
"--url", $PulseUrl,
|
||
"--interval", $Interval
|
||
)
|
||
if ($Token) {
|
||
$serviceArgs += "--token", $Token
|
||
}
|
||
|
||
$serviceBinaryPath = "`"$agentPath`" $($serviceArgs -join ' ')"
|
||
|
||
try {
|
||
if ($existingService) {
|
||
Write-Info "Removing existing service..."
|
||
sc.exe delete $serviceName | Out-Null
|
||
Start-Sleep -Seconds 2
|
||
}
|
||
|
||
# Create the service using New-Service
|
||
New-Service -Name $serviceName `
|
||
-BinaryPathName $serviceBinaryPath `
|
||
-DisplayName "Pulse Host Agent" `
|
||
-Description "Monitors system metrics and reports to Pulse monitoring server" `
|
||
-StartupType Automatic | Out-Null
|
||
|
||
Write-Success "Created Windows service '$serviceName'"
|
||
|
||
# Register Windows Event Log source
|
||
try {
|
||
if (-not ([System.Diagnostics.EventLog]::SourceExists($serviceName))) {
|
||
New-EventLog -LogName Application -Source $serviceName
|
||
Write-Success "Registered Event Log source"
|
||
}
|
||
} catch {
|
||
Write-Warning "Could not register Event Log source (not critical): $_"
|
||
}
|
||
|
||
# Configure service recovery options (restart on failure)
|
||
sc.exe failure $serviceName reset= 86400 actions= restart/60000/restart/60000/restart/60000 | Out-Null
|
||
Write-Success "Configured automatic restart on failure"
|
||
|
||
# Start the service
|
||
Write-Info "Starting service..."
|
||
Start-Service -Name $serviceName
|
||
Start-Sleep -Seconds 3
|
||
|
||
$status = (Get-Service -Name $serviceName).Status
|
||
if ($status -eq 'Running') {
|
||
Write-Success "Service started successfully!"
|
||
|
||
# Optional: Validate that agent is reporting
|
||
Write-Info "Waiting 10 seconds to validate agent reporting..."
|
||
Start-Sleep -Seconds 10
|
||
|
||
# Check Event Log for successful startup
|
||
$recentLogs = Get-EventLog -LogName Application -Source $serviceName -Newest 5 -ErrorAction SilentlyContinue
|
||
$hasStarted = $recentLogs | Where-Object { $_.Message -like "*started successfully*" }
|
||
|
||
if ($hasStarted) {
|
||
Write-Success "Agent is reporting successfully!"
|
||
Write-Info "Check your Pulse dashboard - this host should appear shortly."
|
||
} else {
|
||
Write-Warning "Agent started but validation incomplete. Check Event Viewer if issues occur."
|
||
}
|
||
} else {
|
||
Write-Warning "Service status: $status"
|
||
Write-Info "Checking service logs..."
|
||
Get-EventLog -LogName Application -Source $serviceName -Newest 5 -ErrorAction SilentlyContinue | Format-List TimeGenerated, Message
|
||
}
|
||
|
||
} catch {
|
||
Write-Error "Failed to create/start service: $_"
|
||
Write-Info "You can start the agent manually with:"
|
||
Write-Host " & `"$agentPath`" --url $PulseUrl --interval $Interval $(if ($Token) { '--token ***' })"
|
||
Write-Host ""
|
||
Write-Info "Or check Windows Event Viewer (Application log) for error details."
|
||
exit 1
|
||
}
|
||
} else {
|
||
Write-Info "Skipping service installation (--NoService flag)"
|
||
Write-Host ""
|
||
Write-Info "To start the agent manually:"
|
||
Write-Host " & `"$agentPath`" --url $PulseUrl --interval $Interval $(if ($Token) { '--token ***' })"
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Color $Green "═══════════════════════════════════════════════════════════"
|
||
Write-Success "Installation complete!"
|
||
Write-Color $Green "═══════════════════════════════════════════════════════════"
|
||
Write-Host ""
|
||
|
||
Write-Info "Service Management Commands:"
|
||
Write-Host " Start: Start-Service -Name PulseHostAgent"
|
||
Write-Host " Stop: Stop-Service -Name PulseHostAgent"
|
||
Write-Host " Restart: Restart-Service -Name PulseHostAgent"
|
||
Write-Host " Status: Get-Service -Name PulseHostAgent | Select Status, StartType"
|
||
Write-Host " Remove: sc.exe delete PulseHostAgent"
|
||
Write-Host " Logs: Get-EventLog -LogName Application -Source PulseHostAgent -Newest 50"
|
||
Write-Host ""
|
||
|
||
Write-Info "Files installed:"
|
||
Write-Host " Binary: $agentPath"
|
||
Write-Host " Config: $configPath"
|
||
Write-Host ""
|
||
|
||
Write-Info "The agent is now reporting to: $PulseUrl"
|
||
Write-Host ""
|