diff --git a/cmd/pulse-agent/main.go b/cmd/pulse-agent/main.go index 7a7fa5672..b4fd78490 100644 --- a/cmd/pulse-agent/main.go +++ b/cmd/pulse-agent/main.go @@ -81,6 +81,7 @@ func main() { Interval: cfg.Interval, HostnameOverride: cfg.HostnameOverride, AgentID: cfg.AgentID, // Shared ID? Or separate? Usually separate for now. + AgentType: "unified", Tags: cfg.Tags, InsecureSkipVerify: cfg.InsecureSkipVerify, LogLevel: cfg.LogLevel, @@ -101,7 +102,7 @@ func main() { }) } - // 5. Start Docker Agent (if enabled) + // 6. Start Docker Agent (if enabled) if cfg.EnableDocker { dockerCfg := dockeragent.Config{ PulseURL: cfg.PulseURL, @@ -109,8 +110,9 @@ func main() { Interval: cfg.Interval, HostnameOverride: cfg.HostnameOverride, AgentID: cfg.AgentID, + AgentType: "unified", InsecureSkipVerify: cfg.InsecureSkipVerify, - DisableAutoUpdate: true, // Unified agent handles updates (future) + DisableAutoUpdate: true, // Unified agent handles updates LogLevel: cfg.LogLevel, Logger: &logger, // Docker specific defaults diff --git a/frontend-modern/src/components/Settings/UnifiedAgents.tsx b/frontend-modern/src/components/Settings/UnifiedAgents.tsx index a2158a792..ddcfa55b6 100644 --- a/frontend-modern/src/components/Settings/UnifiedAgents.tsx +++ b/frontend-modern/src/components/Settings/UnifiedAgents.tsx @@ -299,6 +299,14 @@ export const UnifiedAgents: Component = () => { return Array.from(unified.values()).sort((a, b) => a.hostname.localeCompare(b.hostname)); }); + const legacyAgents = createMemo(() => allHosts().filter(h => h.isLegacy)); + const hasLegacyAgents = createMemo(() => legacyAgents().length > 0); + + const getUpgradeCommand = (hostname: string) => { + const token = resolvedToken(); + return `curl -fsSL ${pulseUrl()}/install.sh | sudo bash -s -- --url ${pulseUrl()} --token ${token}`; + }; + const handleRemoveAgent = async (id: string, type: 'host' | 'docker') => { if (!confirm('Are you sure you want to remove this agent? This will stop monitoring but will not uninstall the agent from the remote machine.')) return; @@ -589,6 +597,44 @@ export const UnifiedAgents: Component = () => {

+ +
+
+ + + +
+

+ {legacyAgents().length} legacy agent{legacyAgents().length > 1 ? 's' : ''} detected +

+

+ Legacy agents (pulse-host-agent, pulse-docker-agent) are deprecated. Upgrade to the unified agent for auto-updates and combined host + Docker monitoring. +

+

+ Run this command on each legacy host to upgrade: +

+
+ + {getUpgradeCommand('')} + + +
+
+
+
+
+
diff --git a/internal/dockeragent/agent.go b/internal/dockeragent/agent.go index ade465ed4..7cef43aaa 100644 --- a/internal/dockeragent/agent.go +++ b/internal/dockeragent/agent.go @@ -47,6 +47,7 @@ type Config struct { Interval time.Duration HostnameOverride string AgentID string + AgentType string // "unified" when running as part of pulse-agent, empty for standalone InsecureSkipVerify bool DisableAutoUpdate bool Targets []TargetConfig @@ -656,6 +657,7 @@ func (a *Agent) buildReport(ctx context.Context) (agentsdocker.Report, error) { Agent: agentsdocker.AgentInfo{ ID: agentID, Version: Version, + Type: a.cfg.AgentType, IntervalSeconds: int(a.cfg.Interval / time.Second), }, Host: agentsdocker.HostInfo{ diff --git a/internal/hostagent/agent.go b/internal/hostagent/agent.go index 22ef0ca4e..fe6efc3cd 100644 --- a/internal/hostagent/agent.go +++ b/internal/hostagent/agent.go @@ -27,6 +27,7 @@ type Config struct { Interval time.Duration HostnameOverride string AgentID string + AgentType string // "unified" when running as part of pulse-agent, empty for standalone Tags []string InsecureSkipVerify bool RunOnce bool @@ -241,6 +242,7 @@ func (a *Agent) buildReport(ctx context.Context) (agentshost.Report, error) { Agent: agentshost.AgentInfo{ ID: a.agentID, Version: Version, + Type: a.cfg.AgentType, IntervalSeconds: int(a.interval / time.Second), Hostname: a.hostname, }, diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index 6cd506378..e648e6d39 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -1877,7 +1877,7 @@ func (m *Monitor) ApplyDockerReport(report agentsdocker.Report, tokenRecord *con Services: services, Tasks: tasks, Swarm: swarmInfo, - IsLegacy: isLegacyDockerAgent(report.Agent.Version), + IsLegacy: isLegacyDockerAgent(report.Agent.Type), } if tokenRecord != nil { @@ -2160,7 +2160,7 @@ func (m *Monitor) ApplyHostReport(report agentshost.Report, tokenRecord *config. LastSeen: timestamp, AgentVersion: strings.TrimSpace(report.Agent.Version), Tags: append([]string(nil), report.Tags...), - IsLegacy: isLegacyHostAgent(report.Agent.Version), + IsLegacy: isLegacyHostAgent(report.Agent.Type), } if len(host.LoadAverage) == 0 { @@ -9562,20 +9562,14 @@ func (m *Monitor) checkMockAlerts() { // mock state without needing to grab the alert manager lock again. mock.UpdateAlertSnapshots(m.alertManager.GetActiveAlerts(), m.alertManager.GetRecentlyResolved()) } -func isLegacyHostAgent(version string) bool { - // New unified agent is 1.0.0+ - // Legacy agents are 0.x.x or empty - if version == "" { - return true - } - return strings.HasPrefix(version, "0.") +func isLegacyHostAgent(agentType string) bool { + // Unified agent reports type="unified" + // Legacy standalone agents have empty type + return agentType != "unified" } -func isLegacyDockerAgent(version string) bool { - // New unified agent is 1.0.0+ - // Legacy agents are 0.x.x or empty - if version == "" { - return true - } - return strings.HasPrefix(version, "0.") +func isLegacyDockerAgent(agentType string) bool { + // Unified agent reports type="unified" + // Legacy standalone agents have empty type + return agentType != "unified" } diff --git a/pkg/agents/docker/report.go b/pkg/agents/docker/report.go index 2fa7953b3..ad4765a54 100644 --- a/pkg/agents/docker/report.go +++ b/pkg/agents/docker/report.go @@ -24,6 +24,7 @@ type Report struct { type AgentInfo struct { ID string `json:"id"` Version string `json:"version"` + Type string `json:"type,omitempty"` // "unified", "host", or "docker" - empty means legacy IntervalSeconds int `json:"intervalSeconds"` } diff --git a/pkg/agents/host/report.go b/pkg/agents/host/report.go index df628a1e4..0c209dfb8 100644 --- a/pkg/agents/host/report.go +++ b/pkg/agents/host/report.go @@ -20,6 +20,7 @@ type Report struct { type AgentInfo struct { ID string `json:"id"` Version string `json:"version,omitempty"` + Type string `json:"type,omitempty"` // "unified", "host", or "docker" - empty means legacy IntervalSeconds int `json:"intervalSeconds,omitempty"` Hostname string `json:"hostname,omitempty"` } diff --git a/scripts/install-docker-agent.sh b/scripts/install-docker-agent.sh new file mode 100755 index 000000000..c8b264e02 --- /dev/null +++ b/scripts/install-docker-agent.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# DEPRECATED: pulse-docker-agent installer +# +# This script is deprecated. Please use the unified agent instead: +# +# curl -fsSL http://:7655/install.sh | \ +# sudo bash -s -- --url http://:7655 --token --enable-docker +# +# The unified agent provides: +# - Combined host + Docker monitoring in one binary +# - Automatic updates +# - Simplified management +# +# This script will redirect you to the unified agent installer. +# + +set -euo pipefail + +echo "" +echo "┌─────────────────────────────────────────────────────────────────────────────┐" +echo "│ DEPRECATED: pulse-docker-agent has been replaced by the unified agent. │" +echo "│ │" +echo "│ The unified agent provides: │" +echo "│ • Combined host + Docker monitoring in one binary │" +echo "│ • Automatic updates │" +echo "│ • Simplified management │" +echo "│ │" +echo "│ Installing unified agent instead... │" +echo "└─────────────────────────────────────────────────────────────────────────────┘" +echo "" + +# Parse arguments to extract URL and token for redirection +PULSE_URL="" +PULSE_TOKEN="" + +while [[ $# -gt 0 ]]; do + case $1 in + --url) PULSE_URL="$2"; shift 2 ;; + --token) PULSE_TOKEN="$2"; shift 2 ;; + *) shift ;; # ignore other args + esac +done + +if [[ -z "$PULSE_URL" ]]; then + echo "[ERROR] --url is required" + echo "" + echo "Usage:" + echo " curl -fsSL http://:7655/install.sh | \\" + echo " sudo bash -s -- --url http://:7655 --token --enable-docker" + exit 1 +fi + +if [[ -z "$PULSE_TOKEN" ]]; then + echo "[ERROR] --token is required" + echo "" + echo "Usage:" + echo " curl -fsSL ${PULSE_URL}/install.sh | \\" + echo " sudo bash -s -- --url ${PULSE_URL} --token --enable-docker" + exit 1 +fi + +# Download and run the unified installer with --enable-docker +echo "[INFO] Downloading unified agent installer..." +curl -fsSL "${PULSE_URL}/install.sh" | bash -s -- --url "$PULSE_URL" --token "$PULSE_TOKEN" --enable-docker diff --git a/scripts/install-host-agent.ps1 b/scripts/install-host-agent.ps1 index 8160cddf7..80423be9e 100644 --- a/scripts/install-host-agent.ps1 +++ b/scripts/install-host-agent.ps1 @@ -1,5 +1,19 @@ # Pulse Host Agent Installation Script for Windows # +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ DEPRECATED: This script installs the legacy pulse-host-agent. │ +# │ Please use the unified agent instead: │ +# │ │ +# │ $env:PULSE_URL="http://pulse-server:7655" │ +# │ $env:PULSE_TOKEN="your-token" │ +# │ irm http://pulse-server:7655/install.ps1 | iex │ +# │ │ +# │ The unified agent provides: │ +# │ - Combined host + Docker monitoring in one binary │ +# │ - Automatic updates │ +# │ - Simplified management │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# # Usage: # iwr -useb http://pulse-server:7656/install-host-agent.ps1 | iex # OR with parameters: