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
This commit is contained in:
parent
eb59faaa4a
commit
6795f83e63
7 changed files with 335 additions and 160 deletions
|
|
@ -41,8 +41,12 @@ curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
||||||
| `--token` | `PULSE_TOKEN` | API token | *(required)* |
|
| `--token` | `PULSE_TOKEN` | API token | *(required)* |
|
||||||
| `--interval` | `PULSE_INTERVAL` | Reporting interval | `30s` |
|
| `--interval` | `PULSE_INTERVAL` | Reporting interval | `30s` |
|
||||||
| `--enable-host` | `PULSE_ENABLE_HOST` | Enable host metrics | `true` |
|
| `--enable-host` | `PULSE_ENABLE_HOST` | Enable host metrics | `true` |
|
||||||
| `--enable-docker` | `PULSE_ENABLE_DOCKER` | Enable Docker metrics | `false` |
|
| `--enable-docker` | `PULSE_ENABLE_DOCKER` | Force enable Docker metrics | **auto-detect** |
|
||||||
| `--enable-kubernetes` | `PULSE_ENABLE_KUBERNETES` | Enable Kubernetes metrics | `false` |
|
| `--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)* |
|
| `--kubeconfig` | `PULSE_KUBECONFIG` | Kubeconfig path (optional) | *(auto)* |
|
||||||
| `--kube-context` | `PULSE_KUBE_CONTEXT` | Kubeconfig context (optional) | *(auto)* |
|
| `--kube-context` | `PULSE_KUBE_CONTEXT` | Kubeconfig context (optional) | *(auto)* |
|
||||||
| `--kube-include-namespace` | `PULSE_KUBE_INCLUDE_NAMESPACES` | Limit namespaces (repeatable or CSV) | *(all)* |
|
| `--kube-include-namespace` | `PULSE_KUBE_INCLUDE_NAMESPACES` | Limit namespaces (repeatable or CSV) | *(all)* |
|
||||||
|
|
@ -55,20 +59,36 @@ curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
||||||
| `--agent-id` | `PULSE_AGENT_ID` | Unique agent identifier | *(machine-id)* |
|
| `--agent-id` | `PULSE_AGENT_ID` | Unique agent identifier | *(machine-id)* |
|
||||||
| `--health-addr` | `PULSE_HEALTH_ADDR` | Health/metrics server address | `:9191` |
|
| `--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
|
## Installation Options
|
||||||
|
|
||||||
### Host Monitoring Only (default)
|
### Simple Install (auto-detects everything)
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
||||||
bash -s -- --url http://<pulse-ip>:7655 --token <token>
|
bash -s -- --url http://<pulse-ip>:7655 --token <token>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Host + Docker Monitoring
|
### Force Enable Docker (if auto-detection fails)
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
||||||
bash -s -- --url http://<pulse-ip>:7655 --token <token> --enable-docker
|
bash -s -- --url http://<pulse-ip>:7655 --token <token> --enable-docker
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Disable Docker (even if detected)
|
||||||
|
```bash
|
||||||
|
curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
||||||
|
bash -s -- --url http://<pulse-ip>:7655 --token <token> --disable-docker
|
||||||
|
```
|
||||||
|
|
||||||
### Host + Kubernetes Monitoring
|
### Host + Kubernetes Monitoring
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
curl -fsSL http://<pulse-ip>:7655/install.sh | \
|
||||||
|
|
|
||||||
|
|
@ -486,49 +486,52 @@ export const UnifiedAgents: Component = () => {
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h4 class="text-sm font-semibold text-gray-900 dark:text-gray-100">Installation commands</h4>
|
<div>
|
||||||
<div class="flex items-center gap-4">
|
<h4 class="text-sm font-semibold text-gray-900 dark:text-gray-100">Installation commands</h4>
|
||||||
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer">
|
<p class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">The installer auto-detects Docker, Kubernetes, and Proxmox. Use these to force enable/disable:</p>
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={enableDocker()}
|
|
||||||
onChange={(e) => setEnableDocker(e.currentTarget.checked)}
|
|
||||||
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
|
||||||
/>
|
|
||||||
Docker monitoring
|
|
||||||
</label>
|
|
||||||
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={enableKubernetes()}
|
|
||||||
onChange={(e) => setEnableKubernetes(e.currentTarget.checked)}
|
|
||||||
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
|
||||||
/>
|
|
||||||
Kubernetes monitoring
|
|
||||||
</label>
|
|
||||||
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer" title="For Proxmox VE/PBS nodes - auto-creates API token and registers the node">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={enableProxmox()}
|
|
||||||
onChange={(e) => setEnableProxmox(e.currentTarget.checked)}
|
|
||||||
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
|
||||||
/>
|
|
||||||
Proxmox setup
|
|
||||||
</label>
|
|
||||||
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer" title="Skip TLS certificate verification (for self-signed certificates)">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={insecureMode()}
|
|
||||||
onChange={(e) => setInsecureMode(e.currentTarget.checked)}
|
|
||||||
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
|
||||||
/>
|
|
||||||
Skip TLS verify
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center gap-4 flex-wrap">
|
||||||
|
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer" title="Force enable Docker monitoring (auto-detected if Docker/Podman is available)">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={enableDocker()}
|
||||||
|
onChange={(e) => setEnableDocker(e.currentTarget.checked)}
|
||||||
|
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||||
|
/>
|
||||||
|
Force Docker
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer" title="Force enable Kubernetes monitoring (auto-detected if kubectl/kubeconfig is available)">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={enableKubernetes()}
|
||||||
|
onChange={(e) => setEnableKubernetes(e.currentTarget.checked)}
|
||||||
|
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||||
|
/>
|
||||||
|
Force Kubernetes
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer" title="Force enable Proxmox integration (auto-detected on PVE/PBS hosts)">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={enableProxmox()}
|
||||||
|
onChange={(e) => setEnableProxmox(e.currentTarget.checked)}
|
||||||
|
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||||
|
/>
|
||||||
|
Force Proxmox
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 cursor-pointer" title="Skip TLS certificate verification (for self-signed certificates)">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={insecureMode()}
|
||||||
|
onChange={(e) => setInsecureMode(e.currentTarget.checked)}
|
||||||
|
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||||
|
/>
|
||||||
|
Skip TLS verify
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<Show when={enableProxmox()}>
|
<Show when={enableProxmox()}>
|
||||||
<div class="rounded-lg border border-blue-200 bg-blue-50 px-4 py-3 text-sm text-blue-800 dark:border-blue-800 dark:bg-blue-900/20 dark:text-blue-200">
|
<div class="rounded-lg border border-blue-200 bg-blue-50 px-4 py-3 text-sm text-blue-800 dark:border-blue-800 dark:bg-blue-900/20 dark:text-blue-200">
|
||||||
<p class="font-medium">Proxmox auto-setup enabled</p>
|
<p class="font-medium">Proxmox force-enable</p>
|
||||||
<p class="text-xs mt-1 text-blue-700 dark:text-blue-300">
|
<p class="text-xs mt-1 text-blue-700 dark:text-blue-300">
|
||||||
The agent will create a <code class="bg-blue-100 dark:bg-blue-900/40 px-1 rounded">pulse-monitor</code> user and API token on the Proxmox node,
|
The agent will create a <code class="bg-blue-100 dark:bg-blue-900/40 px-1 rounded">pulse-monitor</code> user and API token on the Proxmox node,
|
||||||
then register it with Pulse automatically. Includes temperature monitoring.
|
then register it with Pulse automatically. Includes temperature monitoring.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { Component, createSignal, createEffect, onCleanup, Show, For } from 'solid-js';
|
import { Component, createSignal, createEffect, onCleanup, Show, For } from 'solid-js';
|
||||||
|
// Note: For is still used for connectedAgents list
|
||||||
import { copyToClipboard } from '@/utils/clipboard';
|
import { copyToClipboard } from '@/utils/clipboard';
|
||||||
import { getPulseBaseUrl } from '@/utils/url';
|
import { getPulseBaseUrl } from '@/utils/url';
|
||||||
import { SecurityAPI } from '@/api/security';
|
import { SecurityAPI } from '@/api/security';
|
||||||
|
import { ProxmoxIcon } from '@/components/icons/ProxmoxIcon';
|
||||||
import type { WizardState } from '../SetupWizard';
|
import type { WizardState } from '../SetupWizard';
|
||||||
|
|
||||||
interface CompleteStepProps {
|
interface CompleteStepProps {
|
||||||
|
|
@ -9,7 +11,7 @@ interface CompleteStepProps {
|
||||||
onComplete: () => void;
|
onComplete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Platform = 'proxmox' | 'docker' | 'kubernetes' | 'host';
|
// Platform auto-detection is now handled by the install script
|
||||||
|
|
||||||
interface ConnectedAgent {
|
interface ConnectedAgent {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -22,18 +24,10 @@ interface ConnectedAgent {
|
||||||
export const CompleteStep: Component<CompleteStepProps> = (props) => {
|
export const CompleteStep: Component<CompleteStepProps> = (props) => {
|
||||||
const [copied, setCopied] = createSignal<'password' | 'token' | 'install' | null>(null);
|
const [copied, setCopied] = createSignal<'password' | 'token' | 'install' | null>(null);
|
||||||
const [showCredentials, setShowCredentials] = createSignal(false);
|
const [showCredentials, setShowCredentials] = createSignal(false);
|
||||||
const [selectedPlatforms, setSelectedPlatforms] = createSignal<Platform[]>([]);
|
|
||||||
const [connectedAgents, setConnectedAgents] = createSignal<ConnectedAgent[]>([]);
|
const [connectedAgents, setConnectedAgents] = createSignal<ConnectedAgent[]>([]);
|
||||||
const [currentInstallToken, setCurrentInstallToken] = createSignal(props.state.apiToken);
|
const [currentInstallToken, setCurrentInstallToken] = createSignal(props.state.apiToken);
|
||||||
const [generatingToken, setGeneratingToken] = createSignal(false);
|
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
|
// Poll for agent connections since WebSocket isn't available during setup
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
let pollInterval: number | undefined;
|
let pollInterval: number | undefined;
|
||||||
|
|
@ -137,14 +131,7 @@ export const CompleteStep: Component<CompleteStepProps> = (props) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const togglePlatform = (platform: Platform) => {
|
// Platform selection removed - installer now auto-detects Docker, Kubernetes, Proxmox
|
||||||
const current = selectedPlatforms();
|
|
||||||
if (current.includes(platform)) {
|
|
||||||
setSelectedPlatforms(current.filter(p => p !== platform));
|
|
||||||
} else {
|
|
||||||
setSelectedPlatforms([...current, platform]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateNewToken = async () => {
|
const generateNewToken = async () => {
|
||||||
if (generatingToken()) return;
|
if (generatingToken()) return;
|
||||||
|
|
@ -219,13 +206,8 @@ Keep these credentials secure!
|
||||||
|
|
||||||
const getInstallCommand = () => {
|
const getInstallCommand = () => {
|
||||||
const baseUrl = getPulseBaseUrl();
|
const baseUrl = getPulseBaseUrl();
|
||||||
// Host monitoring is always enabled by default, only add flags for optional integrations
|
// Simple command - the install script auto-detects Docker, Kubernetes, and Proxmox
|
||||||
const platformFlags = selectedPlatforms()
|
return `curl -sSL ${baseUrl}/install.sh | sudo bash -s -- --url "${baseUrl}" --token "${currentInstallToken()}"`;
|
||||||
.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}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -275,12 +257,44 @@ Keep these credentials secure!
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
{/* Platform selection */}
|
{/* Auto-detection info */}
|
||||||
<div class="bg-white/10 backdrop-blur-xl rounded-xl border border-white/20 p-4 text-left mb-4">
|
<div class="bg-white/10 backdrop-blur-xl rounded-xl border border-white/20 p-4 text-left mb-4">
|
||||||
<h3 class="text-sm font-semibold text-white mb-3">What does this host have?</h3>
|
<h3 class="text-sm font-semibold text-white mb-3 flex items-center gap-2">
|
||||||
|
<svg class="w-4 h-4 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
|
||||||
|
</svg>
|
||||||
|
Smart Auto-Detection
|
||||||
|
</h3>
|
||||||
|
|
||||||
{/* Always included - Host monitoring */}
|
<p class="text-white/70 text-xs mb-3">
|
||||||
<div class="bg-green-500/10 border border-green-400/30 rounded-lg p-2.5 mb-3">
|
The installer automatically detects what's running on each host:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-2 mb-3">
|
||||||
|
<div class="bg-white/5 border border-white/10 rounded-lg p-2 text-center">
|
||||||
|
<div class="h-6 flex items-center justify-center mb-1">
|
||||||
|
<span class="text-lg">🐳</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-white font-medium text-xs">Docker</div>
|
||||||
|
<p class="text-[9px] text-white/40">Container monitoring</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white/5 border border-white/10 rounded-lg p-2 text-center">
|
||||||
|
<div class="h-6 flex items-center justify-center mb-1">
|
||||||
|
<span class="text-lg">☸️</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-white font-medium text-xs">Kubernetes</div>
|
||||||
|
<p class="text-[9px] text-white/40">Cluster monitoring</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white/5 border border-white/10 rounded-lg p-2 text-center">
|
||||||
|
<div class="h-6 flex items-center justify-center mb-1">
|
||||||
|
<ProxmoxIcon class="w-5 h-5 text-orange-400" />
|
||||||
|
</div>
|
||||||
|
<div class="text-white font-medium text-xs">Proxmox</div>
|
||||||
|
<p class="text-[9px] text-white/40">VM & container API</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-green-500/10 border border-green-400/30 rounded-lg p-2.5">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="w-4 h-4 rounded-full bg-green-500 flex items-center justify-center">
|
<div class="w-4 h-4 rounded-full bg-green-500 flex items-center justify-center">
|
||||||
<svg class="w-2.5 h-2.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
|
<svg class="w-2.5 h-2.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
|
||||||
|
|
@ -289,44 +303,13 @@ Keep these credentials secure!
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-white font-medium text-xs">Host Monitoring</span>
|
<span class="text-white font-medium text-xs">Host Metrics</span>
|
||||||
<span class="text-[9px] text-green-300 bg-green-500/20 px-1.5 py-0.5 rounded">Always included</span>
|
<span class="text-[9px] text-green-300 bg-green-500/20 px-1.5 py-0.5 rounded">Always included</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-[10px] text-white/50">CPU, memory, disk, network on any Linux/macOS/Windows server</p>
|
<p class="text-[10px] text-white/50">CPU, memory, disk, network on any Linux/macOS/Windows</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Optional integrations */}
|
|
||||||
<p class="text-[10px] text-white/50 mb-2">Enable if this host runs:</p>
|
|
||||||
<div class="grid grid-cols-3 gap-2">
|
|
||||||
<For each={platforms}>
|
|
||||||
{(platform) => (
|
|
||||||
<button
|
|
||||||
onClick={() => togglePlatform(platform.id)}
|
|
||||||
class={`p-2 rounded-lg text-left transition-all border ${selectedPlatforms().includes(platform.id)
|
|
||||||
? 'bg-blue-500/20 border-blue-400/50'
|
|
||||||
: 'bg-white/5 border-white/10 hover:bg-white/10'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-1.5">
|
|
||||||
<div class={`w-3.5 h-3.5 rounded border-2 flex items-center justify-center ${selectedPlatforms().includes(platform.id)
|
|
||||||
? 'bg-blue-500 border-blue-500'
|
|
||||||
: 'border-white/40'
|
|
||||||
}`}>
|
|
||||||
<Show when={selectedPlatforms().includes(platform.id)}>
|
|
||||||
<svg class="w-2.5 h-2.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
|
||||||
</svg>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<span class="text-white font-medium text-xs">{platform.name}</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-[9px] text-white/40 ml-5 mt-0.5">{platform.desc}</p>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Agent installation */}
|
{/* Agent installation */}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ const (
|
||||||
BcryptCost = 12
|
BcryptCost = 12
|
||||||
|
|
||||||
// MinPasswordLength is the minimum required password length
|
// MinPasswordLength is the minimum required password length
|
||||||
// Set to 1 to allow users to choose their own password security
|
MinPasswordLength = 12
|
||||||
MinPasswordLength = 1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HashPassword generates a bcrypt hash from a plain text password
|
// 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)
|
return fmt.Errorf("password must be at least %d characters long", MinPasswordLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
// That's it - let users choose their own passwords
|
// Let users choose their own passwords beyond length.
|
||||||
// No annoying character type requirements
|
// No character type requirements.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -628,7 +628,7 @@ type Monitor struct {
|
||||||
dockerTokenBindings map[string]string // Track token ID -> agent ID bindings to enforce uniqueness
|
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)
|
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
|
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
|
dockerCommands map[string]*dockerHostCommand
|
||||||
dockerCommandIndex map[string]string
|
dockerCommandIndex map[string]string
|
||||||
guestMetadataMu sync.RWMutex
|
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
|
tokenID := strings.TrimSpace(host.TokenID)
|
||||||
if host.TokenID != "" {
|
hostname := strings.TrimSpace(host.Hostname)
|
||||||
tokenRemoved := m.config.RemoveAPIToken(host.TokenID)
|
|
||||||
if tokenRemoved {
|
|
||||||
m.config.SortAPITokens()
|
|
||||||
m.config.APITokenEnabled = m.config.HasAPITokens()
|
|
||||||
|
|
||||||
if m.persistence != nil {
|
tokenStillUsed := false
|
||||||
if err := m.persistence.SaveAPITokens(m.config.APITokens); err != nil {
|
if tokenID != "" && m.state != nil {
|
||||||
log.Warn().Err(err).Str("tokenID", host.TokenID).Msg("Failed to persist API token revocation after host agent removal")
|
for _, other := range m.state.GetHosts() {
|
||||||
} else {
|
if strings.TrimSpace(other.TokenID) == tokenID {
|
||||||
log.Info().Str("tokenID", host.TokenID).Str("tokenName", host.TokenName).Msg("API token revoked for removed host agent")
|
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()
|
m.mu.Lock()
|
||||||
if _, exists := m.hostTokenBindings[host.TokenID]; exists {
|
if m.hostTokenBindings == nil {
|
||||||
delete(m.hostTokenBindings, host.TokenID)
|
m.hostTokenBindings = make(map[string]string)
|
||||||
log.Debug().
|
}
|
||||||
Str("tokenID", host.TokenID).
|
|
||||||
Str("hostID", hostID).
|
if _, exists := m.hostTokenBindings[tokenID]; exists {
|
||||||
Msg("Unbound host agent token from removed host")
|
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()
|
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)
|
m.state.RemoveConnectionHealth(hostConnectionPrefix + hostID)
|
||||||
|
|
@ -1390,10 +1438,11 @@ func (m *Monitor) RebuildTokenBindings() {
|
||||||
if _, valid := validTokens[tokenID]; !valid {
|
if _, valid := validTokens[tokenID]; !valid {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Use host ID as the binding identifier
|
hostname := strings.TrimSpace(host.Hostname)
|
||||||
if host.ID != "" {
|
if hostname == "" || host.ID == "" {
|
||||||
newHostBindings[tokenID] = host.ID
|
continue
|
||||||
}
|
}
|
||||||
|
newHostBindings[fmt.Sprintf("%s:%s", tokenID, hostname)] = host.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log what changed
|
// Log what changed
|
||||||
|
|
@ -1925,17 +1974,9 @@ func (m *Monitor) ApplyHostReport(report agentshost.Report, tokenRecord *config.
|
||||||
|
|
||||||
existingHosts := m.state.GetHosts()
|
existingHosts := m.state.GetHosts()
|
||||||
|
|
||||||
agentID := strings.TrimSpace(report.Agent.ID)
|
|
||||||
if agentID == "" {
|
|
||||||
agentID = identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
if tokenRecord != nil && tokenRecord.ID != "" {
|
if tokenRecord != nil && tokenRecord.ID != "" {
|
||||||
tokenID := strings.TrimSpace(tokenRecord.ID)
|
tokenID := strings.TrimSpace(tokenRecord.ID)
|
||||||
bindingID := agentID
|
bindingID := identifier
|
||||||
if bindingID == "" {
|
|
||||||
bindingID = identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
if m.hostTokenBindings == nil {
|
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:
|
// Bind tokens by hostname rather than agent ID. This allows:
|
||||||
// - Same host to reconnect after agent reinstall (agent ID changes but hostname doesn't)
|
// - 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)
|
// - 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)
|
bindingKey := fmt.Sprintf("%s:%s", tokenID, hostname)
|
||||||
if boundID, exists := m.hostTokenBindings[bindingKey]; exists && boundID != bindingID {
|
if boundID, exists := m.hostTokenBindings[bindingKey]; exists && boundID != bindingID {
|
||||||
// Same token+hostname but different agent ID - this is a reinstall, allow it
|
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("tokenID", tokenID).
|
Str("tokenID", tokenID).
|
||||||
Str("hostname", hostname).
|
Str("hostname", hostname).
|
||||||
Str("oldAgentID", boundID).
|
Str("previousHostID", boundID).
|
||||||
Str("newAgentID", bindingID).
|
Str("newHostID", bindingID).
|
||||||
Msg("Host agent reinstalled - updating token binding")
|
Msg("Host agent identity changed for token binding; updating binding")
|
||||||
m.hostTokenBindings[bindingKey] = bindingID
|
m.hostTokenBindings[bindingKey] = bindingID
|
||||||
} else if !exists {
|
} else if !exists {
|
||||||
m.hostTokenBindings[bindingKey] = bindingID
|
m.hostTokenBindings[bindingKey] = bindingID
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("tokenID", tokenID).
|
Str("tokenID", tokenID).
|
||||||
Str("agentID", bindingID).
|
Str("hostID", bindingID).
|
||||||
Str("hostname", hostname).
|
Str("hostname", hostname).
|
||||||
Msg("Bound host agent token to hostname")
|
Msg("Bound host agent token to hostname")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ func TestEvaluateHostAgentsClearsAlertWhenHostReturns(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyHostReportRejectsTokenReuseAcrossAgents(t *testing.T) {
|
func TestApplyHostReportAllowsTokenReuseAcrossHosts(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
monitor := &Monitor{
|
monitor := &Monitor{
|
||||||
|
|
@ -164,8 +164,20 @@ func TestApplyHostReportRejectsTokenReuseAcrossAgents(t *testing.T) {
|
||||||
secondReport.Host.Hostname = "host-two"
|
secondReport.Host.Hostname = "host-two"
|
||||||
secondReport.Timestamp = now.Add(30 * time.Second)
|
secondReport.Timestamp = now.Add(30 * time.Second)
|
||||||
|
|
||||||
if _, err := monitor.ApplyHostReport(secondReport, token); err == nil {
|
hostTwo, err := monitor.ApplyHostReport(secondReport, token)
|
||||||
t.Fatalf("expected token reuse across agents to be rejected")
|
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",
|
Hostname: "remove.me",
|
||||||
TokenID: tokenID,
|
TokenID: tokenID,
|
||||||
})
|
})
|
||||||
monitor.hostTokenBindings[tokenID] = "agent-remove"
|
monitor.hostTokenBindings[tokenID+":remove.me"] = hostID
|
||||||
|
monitor.hostTokenBindings[tokenID] = hostID
|
||||||
|
|
||||||
if _, err := monitor.RemoveHostAgent(hostID); err != nil {
|
if _, err := monitor.RemoveHostAgent(hostID); err != nil {
|
||||||
t.Fatalf("RemoveHostAgent: %v", err)
|
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")
|
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) {
|
func TestEvaluateHostAgentsEmptyHostsList(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,22 @@
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# --enable-host Enable host metrics (default: true)
|
# --enable-host Enable host metrics (default: true)
|
||||||
# --enable-docker Enable docker metrics (default: false)
|
# --enable-docker Force enable Docker monitoring (default: auto-detect)
|
||||||
# --enable-kubernetes Enable Kubernetes metrics (default: false)
|
# --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 <dur> Reporting interval (default: 30s)
|
# --interval <dur> Reporting interval (default: 30s)
|
||||||
# --agent-id <id> Custom agent identifier (default: auto-generated)
|
# --agent-id <id> Custom agent identifier (default: auto-generated)
|
||||||
|
# --insecure Skip TLS certificate verification
|
||||||
# --uninstall Remove the agent
|
# --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
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
@ -54,14 +65,19 @@ PULSE_URL=""
|
||||||
PULSE_TOKEN=""
|
PULSE_TOKEN=""
|
||||||
INTERVAL="30s"
|
INTERVAL="30s"
|
||||||
ENABLE_HOST="true"
|
ENABLE_HOST="true"
|
||||||
ENABLE_DOCKER="false"
|
ENABLE_DOCKER="" # Empty means "auto-detect"
|
||||||
ENABLE_KUBERNETES="false"
|
ENABLE_KUBERNETES="" # Empty means "auto-detect"
|
||||||
ENABLE_PROXMOX="false"
|
ENABLE_PROXMOX="" # Empty means "auto-detect"
|
||||||
PROXMOX_TYPE=""
|
PROXMOX_TYPE=""
|
||||||
UNINSTALL="false"
|
UNINSTALL="false"
|
||||||
INSECURE="false"
|
INSECURE="false"
|
||||||
AGENT_ID=""
|
AGENT_ID=""
|
||||||
|
|
||||||
|
# Track if flags were explicitly set (to override auto-detection)
|
||||||
|
DOCKER_EXPLICIT="false"
|
||||||
|
KUBERNETES_EXPLICIT="false"
|
||||||
|
PROXMOX_EXPLICIT="false"
|
||||||
|
|
||||||
# --- Helper Functions ---
|
# --- Helper Functions ---
|
||||||
log_info() { printf "[INFO] %s\n" "$1"; }
|
log_info() { printf "[INFO] %s\n" "$1"; }
|
||||||
log_warn() { printf "[WARN] %s\n" "$1"; }
|
log_warn() { printf "[WARN] %s\n" "$1"; }
|
||||||
|
|
@ -76,6 +92,63 @@ fail() {
|
||||||
exit 1
|
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
|
# Build exec args string for use in service files
|
||||||
# Returns via EXEC_ARGS variable
|
# Returns via EXEC_ARGS variable
|
||||||
build_exec_args() {
|
build_exec_args() {
|
||||||
|
|
@ -120,11 +193,12 @@ while [[ $# -gt 0 ]]; do
|
||||||
--interval) INTERVAL="$2"; shift 2 ;;
|
--interval) INTERVAL="$2"; shift 2 ;;
|
||||||
--enable-host) ENABLE_HOST="true"; shift ;;
|
--enable-host) ENABLE_HOST="true"; shift ;;
|
||||||
--disable-host) ENABLE_HOST="false"; shift ;;
|
--disable-host) ENABLE_HOST="false"; shift ;;
|
||||||
--enable-docker) ENABLE_DOCKER="true"; shift ;;
|
--enable-docker) ENABLE_DOCKER="true"; DOCKER_EXPLICIT="true"; shift ;;
|
||||||
--disable-docker) ENABLE_DOCKER="false"; shift ;;
|
--disable-docker) ENABLE_DOCKER="false"; DOCKER_EXPLICIT="true"; shift ;;
|
||||||
--enable-kubernetes) ENABLE_KUBERNETES="true"; shift ;;
|
--enable-kubernetes) ENABLE_KUBERNETES="true"; KUBERNETES_EXPLICIT="true"; shift ;;
|
||||||
--disable-kubernetes) ENABLE_KUBERNETES="false"; shift ;;
|
--disable-kubernetes) ENABLE_KUBERNETES="false"; KUBERNETES_EXPLICIT="true"; shift ;;
|
||||||
--enable-proxmox) ENABLE_PROXMOX="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 ;;
|
--proxmox-type) PROXMOX_TYPE="$2"; shift 2 ;;
|
||||||
--insecure) INSECURE="true"; shift ;;
|
--insecure) INSECURE="true"; shift ;;
|
||||||
--uninstall) UNINSTALL="true"; shift ;;
|
--uninstall) UNINSTALL="true"; shift ;;
|
||||||
|
|
@ -133,6 +207,47 @@ while [[ $# -gt 0 ]]; do
|
||||||
esac
|
esac
|
||||||
done
|
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 ---
|
# --- Uninstall Logic ---
|
||||||
if [[ "$UNINSTALL" == "true" ]]; then
|
if [[ "$UNINSTALL" == "true" ]]; then
|
||||||
log_info "Uninstalling ${AGENT_NAME} and cleaning up legacy agents..."
|
log_info "Uninstalling ${AGENT_NAME} and cleaning up legacy agents..."
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue