From 6795f83e634ae13d3c6f5624afc268074b88cd6b Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 14 Dec 2025 16:21:59 +0000 Subject: [PATCH] feat: auto-detect platforms during agent install and allow multi-host tokens - Install script now auto-detects Docker, Kubernetes, and Proxmox - Platform monitoring is enabled automatically when detected - Users can override with --disable-* or --enable-* flags - Allow same token to register multiple hosts (one per hostname) - Update tests to reflect new multi-host token behavior - Improve CompleteStep and UnifiedAgents UI components - Update UNIFIED_AGENT.md documentation --- docs/UNIFIED_AGENT.md | 28 +++- .../src/components/Settings/UnifiedAgents.tsx | 81 ++++++----- .../SetupWizard/steps/CompleteStep.tsx | 105 ++++++-------- internal/auth/password.go | 7 +- internal/monitoring/monitor.go | 113 ++++++++++----- .../monitoring/monitor_host_agents_test.go | 26 +++- scripts/install.sh | 135 ++++++++++++++++-- 7 files changed, 335 insertions(+), 160 deletions(-) diff --git a/docs/UNIFIED_AGENT.md b/docs/UNIFIED_AGENT.md index 72700a8..b4714bb 100644 --- a/docs/UNIFIED_AGENT.md +++ b/docs/UNIFIED_AGENT.md @@ -41,8 +41,12 @@ curl -fsSL http://:7655/install.sh | \ | `--token` | `PULSE_TOKEN` | API token | *(required)* | | `--interval` | `PULSE_INTERVAL` | Reporting interval | `30s` | | `--enable-host` | `PULSE_ENABLE_HOST` | Enable host metrics | `true` | -| `--enable-docker` | `PULSE_ENABLE_DOCKER` | Enable Docker metrics | `false` | -| `--enable-kubernetes` | `PULSE_ENABLE_KUBERNETES` | Enable Kubernetes metrics | `false` | +| `--enable-docker` | `PULSE_ENABLE_DOCKER` | Force enable Docker metrics | **auto-detect** | +| `--disable-docker` | - | Disable Docker even if detected | - | +| `--enable-kubernetes` | `PULSE_ENABLE_KUBERNETES` | Force enable Kubernetes metrics | **auto-detect** | +| `--disable-kubernetes` | - | Disable Kubernetes even if detected | - | +| `--enable-proxmox` | `PULSE_ENABLE_PROXMOX` | Force enable Proxmox integration | **auto-detect** | +| `--disable-proxmox` | - | Disable Proxmox even if detected | - | | `--kubeconfig` | `PULSE_KUBECONFIG` | Kubeconfig path (optional) | *(auto)* | | `--kube-context` | `PULSE_KUBE_CONTEXT` | Kubeconfig context (optional) | *(auto)* | | `--kube-include-namespace` | `PULSE_KUBE_INCLUDE_NAMESPACES` | Limit namespaces (repeatable or CSV) | *(all)* | @@ -55,20 +59,36 @@ curl -fsSL http://:7655/install.sh | \ | `--agent-id` | `PULSE_AGENT_ID` | Unique agent identifier | *(machine-id)* | | `--health-addr` | `PULSE_HEALTH_ADDR` | Health/metrics server address | `:9191` | +## Auto-Detection + +The installer automatically detects available platforms on the target machine: + +- **Docker/Podman**: Enabled if `docker info` or `podman info` succeeds +- **Kubernetes**: Enabled if `kubectl cluster-info` succeeds or kubeconfig exists +- **Proxmox**: Enabled if `/etc/pve` or `/etc/proxmox-backup` exists + +Use `--disable-*` flags to skip auto-detected platforms, or `--enable-*` to force enable. + ## Installation Options -### Host Monitoring Only (default) +### Simple Install (auto-detects everything) ```bash curl -fsSL http://:7655/install.sh | \ bash -s -- --url http://:7655 --token ``` -### Host + Docker Monitoring +### Force Enable Docker (if auto-detection fails) ```bash curl -fsSL http://:7655/install.sh | \ bash -s -- --url http://:7655 --token --enable-docker ``` +### Disable Docker (even if detected) +```bash +curl -fsSL http://:7655/install.sh | \ + bash -s -- --url http://:7655 --token --disable-docker +``` + ### Host + Kubernetes Monitoring ```bash curl -fsSL http://:7655/install.sh | \ diff --git a/frontend-modern/src/components/Settings/UnifiedAgents.tsx b/frontend-modern/src/components/Settings/UnifiedAgents.tsx index 848c8a0..4042c7e 100644 --- a/frontend-modern/src/components/Settings/UnifiedAgents.tsx +++ b/frontend-modern/src/components/Settings/UnifiedAgents.tsx @@ -486,49 +486,52 @@ export const UnifiedAgents: Component = () => {
-

Installation commands

-
- - - - +
+

Installation commands

+

The installer auto-detects Docker, Kubernetes, and Proxmox. Use these to force enable/disable:

+
+ + + + +
-

Proxmox auto-setup enabled

+

Proxmox force-enable

The agent will create a pulse-monitor user and API token on the Proxmox node, then register it with Pulse automatically. Includes temperature monitoring. diff --git a/frontend-modern/src/components/SetupWizard/steps/CompleteStep.tsx b/frontend-modern/src/components/SetupWizard/steps/CompleteStep.tsx index 984fdb8..b6e9cfd 100644 --- a/frontend-modern/src/components/SetupWizard/steps/CompleteStep.tsx +++ b/frontend-modern/src/components/SetupWizard/steps/CompleteStep.tsx @@ -1,7 +1,9 @@ import { Component, createSignal, createEffect, onCleanup, Show, For } from 'solid-js'; +// Note: For is still used for connectedAgents list import { copyToClipboard } from '@/utils/clipboard'; import { getPulseBaseUrl } from '@/utils/url'; import { SecurityAPI } from '@/api/security'; +import { ProxmoxIcon } from '@/components/icons/ProxmoxIcon'; import type { WizardState } from '../SetupWizard'; interface CompleteStepProps { @@ -9,7 +11,7 @@ interface CompleteStepProps { onComplete: () => void; } -type Platform = 'proxmox' | 'docker' | 'kubernetes' | 'host'; +// Platform auto-detection is now handled by the install script interface ConnectedAgent { id: string; @@ -22,18 +24,10 @@ interface ConnectedAgent { export const CompleteStep: Component = (props) => { const [copied, setCopied] = createSignal<'password' | 'token' | 'install' | null>(null); const [showCredentials, setShowCredentials] = createSignal(false); - const [selectedPlatforms, setSelectedPlatforms] = createSignal([]); const [connectedAgents, setConnectedAgents] = createSignal([]); const [currentInstallToken, setCurrentInstallToken] = createSignal(props.state.apiToken); const [generatingToken, setGeneratingToken] = createSignal(false); - // Available optional platforms (host monitoring is always enabled) - const platforms = [ - { id: 'proxmox' as Platform, name: 'Proxmox VE', desc: 'VMs & containers via API', icon: '🖥️' }, - { id: 'docker' as Platform, name: 'Docker', desc: 'Container monitoring', icon: '🐳' }, - { id: 'kubernetes' as Platform, name: 'Kubernetes', desc: 'Cluster monitoring', icon: '☸️' }, - ]; - // Poll for agent connections since WebSocket isn't available during setup createEffect(() => { let pollInterval: number | undefined; @@ -137,14 +131,7 @@ export const CompleteStep: Component = (props) => { }); }); - const togglePlatform = (platform: Platform) => { - const current = selectedPlatforms(); - if (current.includes(platform)) { - setSelectedPlatforms(current.filter(p => p !== platform)); - } else { - setSelectedPlatforms([...current, platform]); - } - }; + // Platform selection removed - installer now auto-detects Docker, Kubernetes, Proxmox const generateNewToken = async () => { if (generatingToken()) return; @@ -219,13 +206,8 @@ Keep these credentials secure! const getInstallCommand = () => { const baseUrl = getPulseBaseUrl(); - // Host monitoring is always enabled by default, only add flags for optional integrations - const platformFlags = selectedPlatforms() - .filter(p => p !== 'host') // host is default, don't need flag - .map(p => `--enable-${p}`) - .join(' '); - const flagsPart = platformFlags ? ` ${platformFlags}` : ''; - return `curl -sSL ${baseUrl}/install.sh | sudo bash -s -- --url "${baseUrl}" --token "${currentInstallToken()}"${flagsPart}`; + // Simple command - the install script auto-detects Docker, Kubernetes, and Proxmox + return `curl -sSL ${baseUrl}/install.sh | sudo bash -s -- --url "${baseUrl}" --token "${currentInstallToken()}"`; }; return ( @@ -275,12 +257,44 @@ Keep these credentials secure!

- {/* Platform selection */} + {/* Auto-detection info */}
-

What does this host have?

+

+ + + + Smart Auto-Detection +

- {/* Always included - Host monitoring */} -
+

+ The installer automatically detects what's running on each host: +

+ +
+
+
+ 🐳 +
+
Docker
+

Container monitoring

+
+
+
+ ☸️ +
+
Kubernetes
+

Cluster monitoring

+
+
+
+ +
+
Proxmox
+

VM & container API

+
+
+ +
@@ -289,44 +303,13 @@ Keep these credentials secure!
- Host Monitoring + Host Metrics Always included
-

CPU, memory, disk, network on any Linux/macOS/Windows server

+

CPU, memory, disk, network on any Linux/macOS/Windows

- - {/* Optional integrations */} -

Enable if this host runs:

-
- - {(platform) => ( - - )} - -
{/* Agent installation */} diff --git a/internal/auth/password.go b/internal/auth/password.go index 585798e..47c19ae 100644 --- a/internal/auth/password.go +++ b/internal/auth/password.go @@ -12,8 +12,7 @@ const ( BcryptCost = 12 // MinPasswordLength is the minimum required password length - // Set to 1 to allow users to choose their own password security - MinPasswordLength = 1 + MinPasswordLength = 12 ) // HashPassword generates a bcrypt hash from a plain text password @@ -37,7 +36,7 @@ func ValidatePasswordComplexity(password string) error { return fmt.Errorf("password must be at least %d characters long", MinPasswordLength) } - // That's it - let users choose their own passwords - // No annoying character type requirements + // Let users choose their own passwords beyond length. + // No character type requirements. return nil } diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index fb0ad55..2d7165a 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -628,7 +628,7 @@ type Monitor struct { dockerTokenBindings map[string]string // Track token ID -> agent ID bindings to enforce uniqueness removedKubernetesClusters map[string]time.Time // Track deliberately removed Kubernetes clusters (ID -> removal time) kubernetesTokenBindings map[string]string // Track token ID -> agent ID bindings to enforce uniqueness - hostTokenBindings map[string]string // Track token ID -> agent ID bindings to enforce uniqueness + hostTokenBindings map[string]string // Track tokenID:hostname -> host identity bindings dockerCommands map[string]*dockerHostCommand dockerCommandIndex map[string]string guestMetadataMu sync.RWMutex @@ -1129,33 +1129,81 @@ func (m *Monitor) RemoveHostAgent(hostID string) (models.Host, error) { } } - // Revoke the API token associated with this host agent - if host.TokenID != "" { - tokenRemoved := m.config.RemoveAPIToken(host.TokenID) - if tokenRemoved { - m.config.SortAPITokens() - m.config.APITokenEnabled = m.config.HasAPITokens() + tokenID := strings.TrimSpace(host.TokenID) + hostname := strings.TrimSpace(host.Hostname) - if m.persistence != nil { - if err := m.persistence.SaveAPITokens(m.config.APITokens); err != nil { - log.Warn().Err(err).Str("tokenID", host.TokenID).Msg("Failed to persist API token revocation after host agent removal") - } else { - log.Info().Str("tokenID", host.TokenID).Str("tokenName", host.TokenName).Msg("API token revoked for removed host agent") + tokenStillUsed := false + if tokenID != "" && m.state != nil { + for _, other := range m.state.GetHosts() { + if strings.TrimSpace(other.TokenID) == tokenID { + tokenStillUsed = true + break + } + } + if !tokenStillUsed { + for _, other := range m.state.GetDockerHosts() { + if strings.TrimSpace(other.TokenID) == tokenID { + tokenStillUsed = true + break } } } } - if host.TokenID != "" { + tokenRevoked := false + if tokenID != "" && !tokenStillUsed { + tokenRevoked = m.config.RemoveAPIToken(tokenID) + if tokenRevoked { + m.config.SortAPITokens() + m.config.APITokenEnabled = m.config.HasAPITokens() + + if m.persistence != nil { + if err := m.persistence.SaveAPITokens(m.config.APITokens); err != nil { + log.Warn().Err(err).Str("tokenID", tokenID).Msg("Failed to persist API token revocation after host agent removal") + } else { + log.Info().Str("tokenID", tokenID).Str("tokenName", host.TokenName).Msg("API token revoked for removed host agent") + } + } + } + } else if tokenID != "" && tokenStillUsed { + log.Info(). + Str("tokenID", tokenID). + Str("hostID", hostID). + Msg("API token still used by other agents; skipping revocation during host removal") + } + + if tokenID != "" { m.mu.Lock() - if _, exists := m.hostTokenBindings[host.TokenID]; exists { - delete(m.hostTokenBindings, host.TokenID) - log.Debug(). - Str("tokenID", host.TokenID). - Str("hostID", hostID). - Msg("Unbound host agent token from removed host") + if m.hostTokenBindings == nil { + m.hostTokenBindings = make(map[string]string) + } + + if _, exists := m.hostTokenBindings[tokenID]; exists { + delete(m.hostTokenBindings, tokenID) + } + + if hostname != "" { + key := fmt.Sprintf("%s:%s", tokenID, hostname) + if _, exists := m.hostTokenBindings[key]; exists { + delete(m.hostTokenBindings, key) + } + } + + if tokenRevoked { + prefix := tokenID + ":" + for key := range m.hostTokenBindings { + if strings.HasPrefix(key, prefix) { + delete(m.hostTokenBindings, key) + } + } } m.mu.Unlock() + + log.Debug(). + Str("tokenID", tokenID). + Str("hostID", hostID). + Bool("revoked", tokenRevoked). + Msg("Unbound host agent token bindings after host removal") } m.state.RemoveConnectionHealth(hostConnectionPrefix + hostID) @@ -1390,10 +1438,11 @@ func (m *Monitor) RebuildTokenBindings() { if _, valid := validTokens[tokenID]; !valid { continue } - // Use host ID as the binding identifier - if host.ID != "" { - newHostBindings[tokenID] = host.ID + hostname := strings.TrimSpace(host.Hostname) + if hostname == "" || host.ID == "" { + continue } + newHostBindings[fmt.Sprintf("%s:%s", tokenID, hostname)] = host.ID } // Log what changed @@ -1925,17 +1974,9 @@ func (m *Monitor) ApplyHostReport(report agentshost.Report, tokenRecord *config. existingHosts := m.state.GetHosts() - agentID := strings.TrimSpace(report.Agent.ID) - if agentID == "" { - agentID = identifier - } - if tokenRecord != nil && tokenRecord.ID != "" { tokenID := strings.TrimSpace(tokenRecord.ID) - bindingID := agentID - if bindingID == "" { - bindingID = identifier - } + bindingID := identifier m.mu.Lock() if m.hostTokenBindings == nil { @@ -1944,22 +1985,20 @@ func (m *Monitor) ApplyHostReport(report agentshost.Report, tokenRecord *config. // Bind tokens by hostname rather than agent ID. This allows: // - Same host to reconnect after agent reinstall (agent ID changes but hostname doesn't) // - Multiple hosts to use the same token (each hostname gets its own binding entry) - // - Prevents a stolen token from being used on a different hostname bindingKey := fmt.Sprintf("%s:%s", tokenID, hostname) if boundID, exists := m.hostTokenBindings[bindingKey]; exists && boundID != bindingID { - // Same token+hostname but different agent ID - this is a reinstall, allow it log.Info(). Str("tokenID", tokenID). Str("hostname", hostname). - Str("oldAgentID", boundID). - Str("newAgentID", bindingID). - Msg("Host agent reinstalled - updating token binding") + Str("previousHostID", boundID). + Str("newHostID", bindingID). + Msg("Host agent identity changed for token binding; updating binding") m.hostTokenBindings[bindingKey] = bindingID } else if !exists { m.hostTokenBindings[bindingKey] = bindingID log.Debug(). Str("tokenID", tokenID). - Str("agentID", bindingID). + Str("hostID", bindingID). Str("hostname", hostname). Msg("Bound host agent token to hostname") } diff --git a/internal/monitoring/monitor_host_agents_test.go b/internal/monitoring/monitor_host_agents_test.go index 19613bb..0f13dd8 100644 --- a/internal/monitoring/monitor_host_agents_test.go +++ b/internal/monitoring/monitor_host_agents_test.go @@ -117,7 +117,7 @@ func TestEvaluateHostAgentsClearsAlertWhenHostReturns(t *testing.T) { } } -func TestApplyHostReportRejectsTokenReuseAcrossAgents(t *testing.T) { +func TestApplyHostReportAllowsTokenReuseAcrossHosts(t *testing.T) { t.Helper() monitor := &Monitor{ @@ -164,8 +164,20 @@ func TestApplyHostReportRejectsTokenReuseAcrossAgents(t *testing.T) { secondReport.Host.Hostname = "host-two" secondReport.Timestamp = now.Add(30 * time.Second) - if _, err := monitor.ApplyHostReport(secondReport, token); err == nil { - t.Fatalf("expected token reuse across agents to be rejected") + hostTwo, err := monitor.ApplyHostReport(secondReport, token) + if err != nil { + t.Fatalf("ApplyHostReport hostTwo: %v", err) + } + if hostTwo.ID == "" { + t.Fatalf("expected hostTwo to have an identifier") + } + if hostTwo.ID == hostOne.ID { + t.Fatalf("expected different host IDs for different machines, got %q", hostTwo.ID) + } + + snapshot := monitor.state.GetSnapshot() + if got := len(snapshot.Hosts); got != 2 { + t.Fatalf("expected 2 hosts in state, got %d", got) } } @@ -187,15 +199,19 @@ func TestRemoveHostAgentUnbindsToken(t *testing.T) { Hostname: "remove.me", TokenID: tokenID, }) - monitor.hostTokenBindings[tokenID] = "agent-remove" + monitor.hostTokenBindings[tokenID+":remove.me"] = hostID + monitor.hostTokenBindings[tokenID] = hostID if _, err := monitor.RemoveHostAgent(hostID); err != nil { t.Fatalf("RemoveHostAgent: %v", err) } - if _, exists := monitor.hostTokenBindings[tokenID]; exists { + if _, exists := monitor.hostTokenBindings[tokenID+":remove.me"]; exists { t.Fatalf("expected token binding to be cleared after host removal") } + if _, exists := monitor.hostTokenBindings[tokenID]; exists { + t.Fatalf("expected legacy token binding to be cleared after host removal") + } } func TestEvaluateHostAgentsEmptyHostsList(t *testing.T) { diff --git a/scripts/install.sh b/scripts/install.sh index 2cb5441..8a14f7f 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -8,11 +8,22 @@ # # Options: # --enable-host Enable host metrics (default: true) -# --enable-docker Enable docker metrics (default: false) -# --enable-kubernetes Enable Kubernetes metrics (default: false) +# --enable-docker Force enable Docker monitoring (default: auto-detect) +# --disable-docker Disable Docker monitoring even if detected +# --enable-kubernetes Force enable Kubernetes monitoring (default: auto-detect) +# --disable-kubernetes Disable Kubernetes monitoring even if detected +# --enable-proxmox Force enable Proxmox integration (default: auto-detect) +# --disable-proxmox Disable Proxmox integration even if detected # --interval Reporting interval (default: 30s) # --agent-id Custom agent identifier (default: auto-generated) +# --insecure Skip TLS certificate verification # --uninstall Remove the agent +# +# Auto-Detection: +# The installer automatically detects Docker, Kubernetes, and Proxmox on the +# target machine and enables monitoring for detected platforms. Use --disable-* +# flags to skip specific platforms, or --enable-* to force enable even if not +# detected. set -euo pipefail @@ -54,14 +65,19 @@ PULSE_URL="" PULSE_TOKEN="" INTERVAL="30s" ENABLE_HOST="true" -ENABLE_DOCKER="false" -ENABLE_KUBERNETES="false" -ENABLE_PROXMOX="false" +ENABLE_DOCKER="" # Empty means "auto-detect" +ENABLE_KUBERNETES="" # Empty means "auto-detect" +ENABLE_PROXMOX="" # Empty means "auto-detect" PROXMOX_TYPE="" UNINSTALL="false" INSECURE="false" AGENT_ID="" +# Track if flags were explicitly set (to override auto-detection) +DOCKER_EXPLICIT="false" +KUBERNETES_EXPLICIT="false" +PROXMOX_EXPLICIT="false" + # --- Helper Functions --- log_info() { printf "[INFO] %s\n" "$1"; } log_warn() { printf "[WARN] %s\n" "$1"; } @@ -76,6 +92,63 @@ fail() { exit 1 } +# --- Auto-Detection Functions --- +detect_docker() { + # Check if Docker is available and accessible + if command -v docker &>/dev/null; then + # Try to connect to Docker daemon + if docker info &>/dev/null 2>&1; then + return 0 + fi + fi + # Also check for Podman (Docker-compatible) + if command -v podman &>/dev/null; then + if podman info &>/dev/null 2>&1; then + return 0 + fi + fi + return 1 +} + +detect_kubernetes() { + # Check for kubectl and cluster access + if command -v kubectl &>/dev/null; then + # Try to connect to cluster (quick timeout) + if timeout 3 kubectl cluster-info &>/dev/null 2>&1; then + return 0 + fi + fi + # Check for kubeconfig file + if [[ -f "${HOME}/.kube/config" ]] || [[ -f "/etc/kubernetes/admin.conf" ]]; then + return 0 + fi + # Check if running inside a Kubernetes pod + if [[ -f "/var/run/secrets/kubernetes.io/serviceaccount/token" ]]; then + return 0 + fi + return 1 +} + +detect_proxmox() { + # Check for Proxmox VE + if [[ -d "/etc/pve" ]]; then + return 0 + fi + # Check for Proxmox Backup Server + if [[ -d "/etc/proxmox-backup" ]]; then + return 0 + fi + # Check for pveversion command + if command -v pveversion &>/dev/null; then + return 0 + fi + # Check for proxmox-backup-manager command + if command -v proxmox-backup-manager &>/dev/null; then + return 0 + fi + return 1 +} + # Build exec args string for use in service files # Returns via EXEC_ARGS variable build_exec_args() { @@ -120,11 +193,12 @@ while [[ $# -gt 0 ]]; do --interval) INTERVAL="$2"; shift 2 ;; --enable-host) ENABLE_HOST="true"; shift ;; --disable-host) ENABLE_HOST="false"; shift ;; - --enable-docker) ENABLE_DOCKER="true"; shift ;; - --disable-docker) ENABLE_DOCKER="false"; shift ;; - --enable-kubernetes) ENABLE_KUBERNETES="true"; shift ;; - --disable-kubernetes) ENABLE_KUBERNETES="false"; shift ;; - --enable-proxmox) ENABLE_PROXMOX="true"; shift ;; + --enable-docker) ENABLE_DOCKER="true"; DOCKER_EXPLICIT="true"; shift ;; + --disable-docker) ENABLE_DOCKER="false"; DOCKER_EXPLICIT="true"; shift ;; + --enable-kubernetes) ENABLE_KUBERNETES="true"; KUBERNETES_EXPLICIT="true"; shift ;; + --disable-kubernetes) ENABLE_KUBERNETES="false"; KUBERNETES_EXPLICIT="true"; shift ;; + --enable-proxmox) ENABLE_PROXMOX="true"; PROXMOX_EXPLICIT="true"; shift ;; + --disable-proxmox) ENABLE_PROXMOX="false"; PROXMOX_EXPLICIT="true"; shift ;; --proxmox-type) PROXMOX_TYPE="$2"; shift 2 ;; --insecure) INSECURE="true"; shift ;; --uninstall) UNINSTALL="true"; shift ;; @@ -133,6 +207,47 @@ while [[ $# -gt 0 ]]; do esac done +# --- Platform Auto-Detection --- +# Only auto-detect if flags weren't explicitly set +log_info "Detecting available platforms..." + +if [[ "$DOCKER_EXPLICIT" != "true" ]]; then + if detect_docker; then + log_info "Docker/Podman detected - enabling container monitoring" + log_info " (use --disable-docker to skip)" + ENABLE_DOCKER="true" + else + ENABLE_DOCKER="false" + fi +fi + +if [[ "$KUBERNETES_EXPLICIT" != "true" ]]; then + if detect_kubernetes; then + log_info "Kubernetes detected - enabling cluster monitoring" + log_info " (use --disable-kubernetes to skip)" + ENABLE_KUBERNETES="true" + else + ENABLE_KUBERNETES="false" + fi +fi + +if [[ "$PROXMOX_EXPLICIT" != "true" ]]; then + if detect_proxmox; then + log_info "Proxmox detected - enabling Proxmox integration" + log_info " (use --disable-proxmox to skip)" + ENABLE_PROXMOX="true" + else + ENABLE_PROXMOX="false" + fi +fi + +# Summary of what will be monitored +log_info "Monitoring configuration:" +log_info " Host metrics: $ENABLE_HOST" +log_info " Docker/Podman: $ENABLE_DOCKER" +log_info " Kubernetes: $ENABLE_KUBERNETES" +log_info " Proxmox: $ENABLE_PROXMOX" + # --- Uninstall Logic --- if [[ "$UNINSTALL" == "true" ]]; then log_info "Uninstalling ${AGENT_NAME} and cleaning up legacy agents..."