mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 00:17:39 +01:00
feat: improve legacy agent detection and migration UX
Add seamless migration path from legacy agents to unified agent: - Add AgentType field to report payloads (unified vs legacy detection) - Update server to detect legacy agents by type instead of version - Add UI banner showing upgrade command when legacy agents are detected - Add deprecation notice to install-host-agent.ps1 - Create install-docker-agent.sh stub that redirects to unified installer Legacy agents (pulse-host-agent, pulse-docker-agent) now show a "Legacy" badge in the UI with a one-click copy command to upgrade to the unified agent.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Show when={hasLegacyAgents()}>
|
||||
<div class="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 dark:border-amber-700 dark:bg-amber-900/20">
|
||||
<div class="flex items-start gap-3">
|
||||
<svg class="h-5 w-5 flex-shrink-0 text-amber-500 dark:text-amber-400 mt-0.5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<div class="flex-1 space-y-2">
|
||||
<p class="text-sm font-medium text-amber-800 dark:text-amber-200">
|
||||
{legacyAgents().length} legacy agent{legacyAgents().length > 1 ? 's' : ''} detected
|
||||
</p>
|
||||
<p class="text-sm text-amber-700 dark:text-amber-300">
|
||||
Legacy agents (pulse-host-agent, pulse-docker-agent) are deprecated. Upgrade to the unified agent for auto-updates and combined host + Docker monitoring.
|
||||
</p>
|
||||
<p class="text-xs text-amber-600 dark:text-amber-400">
|
||||
Run this command on each legacy host to upgrade:
|
||||
</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<code class="flex-1 break-all rounded bg-amber-100 px-3 py-2 font-mono text-xs text-amber-900 dark:bg-amber-900/40 dark:text-amber-100">
|
||||
{getUpgradeCommand('')}
|
||||
</code>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
const success = await copyToClipboard(getUpgradeCommand(''));
|
||||
if (typeof window !== 'undefined' && window.showToast) {
|
||||
window.showToast(success ? 'success' : 'error', success ? 'Copied!' : 'Failed to copy');
|
||||
}
|
||||
}}
|
||||
class="rounded-lg bg-amber-200 px-3 py-1.5 text-xs font-medium text-amber-800 transition-colors hover:bg-amber-300 dark:bg-amber-800 dark:text-amber-100 dark:hover:bg-amber-700"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
65
scripts/install-docker-agent.sh
Executable file
65
scripts/install-docker-agent.sh
Executable file
@@ -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://<pulse-server>:7655/install.sh | \
|
||||
# sudo bash -s -- --url http://<pulse-server>:7655 --token <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://<pulse-server>:7655/install.sh | \\"
|
||||
echo " sudo bash -s -- --url http://<pulse-server>:7655 --token <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 <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
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user