From 5c4be1921c3dbf9ad54ab0017c3544a599f449a4 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 2 Nov 2025 22:47:55 +0000 Subject: [PATCH] chore: snapshot current changes --- Dockerfile | 2 + docs/DOCKER_MONITORING.md | 53 +- docs/TEMPERATURE_MONITORING.md | 7 + frontend-modern/pnpm-lock.yaml | Bin 0 -> 122715 bytes frontend-modern/src/api/monitoring.ts | 70 + .../src/components/Alerts/ResourceTable.tsx | 210 +- .../src/components/Alerts/ThresholdsTable.tsx | 38 +- .../components/Dashboard/ThresholdSlider.tsx | 19 +- .../components/Docker/DockerUnifiedTable.tsx | 54 +- .../src/components/Settings/DockerAgents.tsx | 469 +- .../src/components/Settings/HostAgents.tsx | 442 +- .../src/components/Settings/Settings.tsx | 7841 +++++++++-------- .../Settings/__tests__/DockerAgents.test.tsx | 218 + .../Settings/__tests__/HostAgents.test.tsx | 142 +- frontend-modern/src/pages/Alerts.tsx | 2 +- frontend-modern/src/types/alerts.ts | 1 + frontend-modern/src/types/api.ts | 10 + go.mod | 1 + internal/alerts/alerts.go | 20 + internal/api/diagnostics.go | 117 + internal/api/router.go | 31 +- internal/discovery/service.go | 213 +- internal/discovery/service_test.go | 155 + internal/dockeragent/agent.go | 119 +- .../dockeragent/agent_integration_test.go | 162 + internal/models/converters.go | 16 +- internal/models/models.go | 107 +- internal/models/models_frontend.go | 57 +- internal/models/state_snapshot.go | 118 +- internal/monitoring/docker_commands_test.go | 27 + internal/monitoring/monitor.go | 52 +- internal/monitoring/monitor_docker_test.go | 4 + internal/monitoring/temperature.go | 4 +- scripts/hot-dev.sh | 19 +- scripts/install-docker-agent-v2.sh | 121 +- scripts/install-docker-agent.sh | 355 +- scripts/install-host-agent.sh | 61 +- scripts/install-sensor-proxy.sh | 126 +- 38 files changed, 7361 insertions(+), 4102 deletions(-) create mode 100644 frontend-modern/pnpm-lock.yaml create mode 100644 frontend-modern/src/components/Settings/__tests__/DockerAgents.test.tsx create mode 100644 internal/discovery/service_test.go create mode 100644 internal/dockeragent/agent_integration_test.go diff --git a/Dockerfile b/Dockerfile index 9fa4fd5..8ec5133 100644 --- a/Dockerfile +++ b/Dockerfile @@ -113,6 +113,8 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \ COPY --from=backend-builder /app/VERSION /VERSION +ENV PULSE_NO_AUTO_UPDATE=true + ENTRYPOINT ["/usr/local/bin/pulse-docker-agent"] # Final stage (Pulse server runtime) diff --git a/docs/DOCKER_MONITORING.md b/docs/DOCKER_MONITORING.md index 87698af..9b8f0ad 100644 --- a/docs/DOCKER_MONITORING.md +++ b/docs/DOCKER_MONITORING.md @@ -1,6 +1,6 @@ # Docker Monitoring Agent -Pulse is focused on Proxmox VE and PBS, but many homelabs also run application stacks in Docker. The optional Pulse Docker agent turns container health and resource usage into first-class metrics that show up alongside your hypervisor data. +Pulse is focused on Proxmox VE and PBS, but many homelabs also run application stacks in Docker. The optional Pulse Docker agent turns container health and resource usage into first-class metrics that show up alongside your hypervisor data. The recommended deployment is the bundled, least-privilege systemd service that runs the static `pulse-docker-agent` binary directly on the host. That path lets the installer lock down permissions, manage upgrades automatically, and integrate with the native init system. Containerising the agent is still available for orchestrated environments, but it trades away some of those controls (and still needs the Docker socket) so treat that option as advanced. ## What the agent reports @@ -12,6 +12,7 @@ Every check interval (30s by default) the agent collects: - CPU usage, memory consumption and limits - Images, port mappings, network addresses, and start times - Writable layer size, root filesystem size, block I/O totals, and mount metadata (shown in the Docker table drawer) +- Read/write throughput derived from Docker block I/O counters so you can spot noisy workloads at a glance - Health-check failures, restart-loop windows, and recent exit codes (displayed in the UI under each container drawer) Data is pushed to Pulse over HTTPS using your existing API token – no inbound firewall rules required. @@ -38,7 +39,7 @@ Copy the binary to your Docker host (e.g. `/usr/local/bin/pulse-docker-agent`) a > **Why `CGO_ENABLED=0`?** Building a fully static binary ensures the agent runs on hosts still using older glibc releases (for example Debian 11 with glibc 2.31). -### Quick install from your Pulse server +### Quick install from your Pulse server (recommended) Use the bundled installation script (ships with Pulse v4.22.0+) to deploy and manage the agent. Replace the token placeholder with an API token generated in **Settings → Security**. Create a dedicated token for each Docker host so you can revoke individual credentials without touching others—sharing one token across many hosts makes incident response much harder. Tokens used here should include the `docker:report` scope so the agent can submit telemetry (add `docker:manage` only if you plan to issue lifecycle commands remotely). @@ -49,6 +50,10 @@ curl -fsSL http://pulse.example.com/install-docker-agent.sh \ > **Why sudo?** The installer needs to drop binaries under `/usr/local/bin`, create a systemd service, and start it—actions that require root privileges. Piping to `sudo bash …` saves you from retrying if you run the command as an unprivileged user. +The script stores credentials in `/etc/pulse/pulse-docker-agent.env` (mode `600`) and creates a locked-down `pulse-docker` service account that only needs access to the Docker socket. Rotate tokens by editing that env file and running `sudo systemctl restart pulse-docker-agent`. + +To keep remote stop/remove commands working from Pulse, the installer also drops a small polkit rule that lets the `pulse-docker` service account run `systemctl stop/disable pulse-docker-agent` without password prompts. If you remove that rule, expect to acknowledge stop requests manually with `sudo systemctl disable --now pulse-docker-agent`. + Running the one-liner again from another Pulse server (with its own URL/token) will merge that server into the same agent automatically—no extra flags required. To report to more than one Pulse instance from the same Docker host, repeat the `--target` flag (format: `https://pulse.example.com|`) or export `PULSE_TARGETS` before running the script: @@ -112,32 +117,58 @@ A single `pulse-docker-agent` process can now serve any number of Pulse backends ```ini [Unit] Description=Pulse Docker Agent -After=network.target docker.service -Requires=docker.service +After=network-online.target docker.socket docker.service +Wants=network-online.target docker.socket [Service] Type=simple -Environment=PULSE_URL=https://pulse.example.com -Environment=PULSE_TOKEN=replace-me -Environment=PULSE_TARGETS=https://pulse.example.com|replace-me;https://pulse-dr.example.com|replace-me-dr +EnvironmentFile=-/etc/pulse/pulse-docker-agent.env ExecStart=/usr/local/bin/pulse-docker-agent --interval 30s -Restart=always -RestartSec=5 +Restart=on-failure +RestartSec=5s +StartLimitIntervalSec=120 +StartLimitBurst=5 +User=pulse-docker +Group=pulse-docker +SupplementaryGroups=docker +UMask=0077 +NoNewPrivileges=yes +RestrictSUIDSGID=yes +RestrictRealtime=yes +PrivateTmp=yes +ProtectSystem=full +ProtectHome=read-only +ProtectControlGroups=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectKernelLogs=yes +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +ReadWritePaths=/var/run/docker.sock +ProtectHostname=yes +ProtectClock=yes [Install] WantedBy=multi-user.target ``` -### Containerised agent (advanced) +Rotate credentials or add additional Pulse targets by editing `/etc/pulse/pulse-docker-agent.env` and reloading the service with `sudo systemctl restart pulse-docker-agent`. + +### Containerised agent (advanced / optional) If you prefer to run the agent inside a container, mount the Docker socket and supply the same environment variables: ```bash docker run -d \ --name pulse-docker-agent \ + --pid=host \ + --uts=host \ -e PULSE_URL="https://pulse.example.com" \ -e PULSE_TOKEN="" \ -e PULSE_TARGETS="https://pulse.example.com|;https://pulse-dr.example.com|" \ + -e PULSE_NO_AUTO_UPDATE=true \ + -v /etc/machine-id:/etc/machine-id:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ --restart unless-stopped \ ghcr.io/rcourtman/pulse-docker-agent:latest @@ -145,6 +176,8 @@ docker run -d \ > **Note**: Official images for `linux/amd64` and `linux/arm64` are published to `ghcr.io/rcourtman/pulse-docker-agent`. To test local changes, run `docker build --target agent_runtime -t pulse-docker-agent:test .` from the repository root. +`--pid=host`, `--uts=host`, and the `/etc/machine-id` bind keep host metadata stable so Pulse doesn’t think the container itself is the Docker host. Auto-update is disabled in the image by default; rebuild or override `PULSE_NO_AUTO_UPDATE=false` only if you manage upgrades outside of your orchestrator. Expect to grant the container the same level of Docker socket access as the systemd service—running inside Docker doesn’t sandbox the agent from the host. + ## Configuration reference | Flag / Env var | Description | Default | diff --git a/docs/TEMPERATURE_MONITORING.md b/docs/TEMPERATURE_MONITORING.md index a9c1d69..5031d1f 100644 --- a/docs/TEMPERATURE_MONITORING.md +++ b/docs/TEMPERATURE_MONITORING.md @@ -173,6 +173,13 @@ The auto-setup script (Settings → Nodes → Setup Script) will prompt you to c If the node is part of a Proxmox cluster, the script will now detect the other members and offer to configure the same SSH/lm-sensors setup on each of them automatically—confirm when prompted to roll it out cluster-wide. +### Host-side responsibilities + +- Run the host installer (`install-sensor-proxy.sh`) on the Proxmox machine that hosts Pulse to install and maintain the `pulse-sensor-proxy` service. The node setup script does not create this service. +- Re-run the host installer if the service or socket disappears after a host upgrade or configuration cleanup; the installer is idempotent. +- The installer now ships a self-heal timer (`pulse-sensor-proxy-selfheal.timer`) that restarts or reinstalls the proxy if it ever goes missing; leave it enabled for automatic recovery. +- Hot dev builds now warn when only a container-local proxy socket is present, signalling that the host proxy needs to be reinstalled before temperatures will flow back into Pulse. + ### Turnkey Setup for Standalone Nodes (v4.25.0+) **For standalone nodes** (not in a Proxmox cluster) running **containerized Pulse**, the setup script now automatically configures temperature monitoring with zero manual steps: diff --git a/frontend-modern/pnpm-lock.yaml b/frontend-modern/pnpm-lock.yaml new file mode 100644 index 0000000000000000000000000000000000000000..847fae7ea2d151f7ca6b0fc5bbd907708e177e52 GIT binary patch literal 122715 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

+LxHq+aS}|A7I}r&`)u%+feJ< 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ⅆ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�otxPMNOA;-?(fqwBf2eI=SSG#{Pm zV&>aJ8B>JI#BnQ`&_3XgBgZHA*E@fsiUB13b35GY>jy3kGJX<#SiQI`#PTIVxYR2i zN*wba?~%-q$G(ly7PxCEWevOc4HKd)zi?Lq88WPWld(Dle$mv@6|2pQIZ%y;yTL4Y zABWggkKw^uNeg|)UT=#NOyT5fmfgfT(j5{otm%{082@O6#0Ri&1)sCW2>?aQ{XuCG4#g_dAa=hD#DLB;l!9x<&w&bL6 z`Rn=qFe|osm>5Wp0U%*dZOCPF$8nSuIB<+$F}Zbw7yOXwgE{T0TpVWR9eH#KDwpeo zUmnuex49DQhsGv|Wyeh~TuZ?76ND(t~rHuPcOg8(9GIP6I85WQGoEc3j~ zC{l>xkfdVcoZz{xz>t4CuPknlc{G7fha1RJGz1%Lr{J&n0eJCn3?Q$34Ns6CYSU!( z0oxcb>{MsrujY!c^utaTyleuUXonx#PNwE)m7|eNO%HDDo!Mpi_K?mmxjsFi=nfDt zciYMdz)$#SW-1e#^ThV@)P6%@Y4>5-oJrga-=wy=xsxz)lO5ar1KI7knUAkYGI~!# z4L8%_g=cm;No=;ojFa)bjp%Ne!{jCMAiQi|cQ12O6uk%Hyfcs84ZX7g|v&^%Y;o}#z;-lkqOY6Y(MMp_UB@V9%PK2lJK@4T!aE>XS z;9!7_c(LmTZw^6((sW~Wi}GP1_#|!s;W6S_CIAx$(FPMwiFU^q27b^(5vNdnLN74( z5UVfHVjGP)yI}l=z}e|KGcMg&zN3ttTbiREr~n@wCEI80jS*HZ;1P@7ynclKa_j6*FLZa>|yOtU$D*&(vXu78DeU{vyD zZeh3`pH@hEy$EMLC8AwO$nuh<{6NZ2rsIi^Gd#eI{=jDO1201e^1^lY`dd%-*rpe- z3L=-+FS`w^QWKQi&S6(1$`iEjwvgi!jt!yeogd_x1+T&F{6?YF*RUZCk@y2-OewHD zgwnE#6wZnl1m+Fb%$G%r0*?}i{$@Q?!o)kQTv`=r>3@h%ef*+3+x`Q!AkEt!+I(Sc z5fuS>#eQhQBk%g-Lq(M0bgE8oPZ1J&a11)kp};%;b_!$mTM*x%P2T=|a__K>lO_0A z`8&GcKs(q%IC3Zk8yy)9)PD23I}$xMp!#N^+}8vkRnzAs1{o{zGu@Oo41yE~>GPKB zCd%uP1zME!BkG_qnYU{C33R!GSV%6sIlrDNv=E7%3ORA|XBxSf{s;8<>leK=JJ4$I z&o|M{zUyu2ezoC1Fl$1^rg-O6xOD@%HwkhuWH7XU#2Q2i7j{cxL!{vR6On@Rx~JkS zxA+tPCYOTPFcK)Zgv0OXAphYDjN<+>S-EK7Q1%cK6W$vXIY2IPUm$LF0JHu6j`IWN z1>iS{P=I5K8s2a0X$A9VcOomW&N2>(N08lx6O}Utkf`@oLL3qZ#eKN% z$_MeA&th^*8f^fX(!|_!E|{7?b3jjUJSH`Pea;5t#UbL#21)mVw>c&;6m2`!PaHxy z`!@-f!%r_U8AXuL8}uvSI4v&la6Z|{u^_$|w#ypA7A>8@@qNwo@Vh-7zOtK8PXDY$#fN#9=7?BPe6ip`-=evk> z$fe+*ufIjseV*1t6pM?SZmqk(bx7ooAArpLo0kK8J-G#yjY|;HbnpnhL&;<{7MVKh zWqUMnyyU51us+ZzrT~e)1`9EoOIoTzOk=2ASUsDs9 zf4Py&9?UX2;B;aY8`w~{9h~V)Hrv11s!903sbO%~A4Fn+*UDTUB { + const url = `${this.baseUrl}/agents/docker/hosts/${encodeURIComponent(hostId)}/allow-reenroll`; + + const response = await apiFetch(url, { + method: 'POST', + }); + + if (!response.ok) { + let message = `Failed with status ${response.status}`; + try { + const text = await response.text(); + if (text?.trim()) { + message = text.trim(); + try { + const parsed = JSON.parse(text); + if (typeof parsed?.error === 'string' && parsed.error.trim()) { + message = parsed.error.trim(); + } + } catch (_err) { + // ignore parse error, use raw text + } + } + } catch (_err) { + // ignore read error + } + + throw new Error(message); + } + } + + static async deleteHostAgent(hostId: string): Promise { + if (!hostId) { + throw new Error('Host ID is required to remove a host agent.'); + } + + const url = `${this.baseUrl}/agents/host/${encodeURIComponent(hostId)}`; + const response = await apiFetch(url, { method: 'DELETE' }); + + if (!response.ok) { + let message = `Failed with status ${response.status}`; + try { + const text = await response.text(); + if (text?.trim()) { + message = text.trim(); + try { + const parsed = JSON.parse(text); + if (typeof parsed?.error === 'string' && parsed.error.trim()) { + message = parsed.error.trim(); + } else if (typeof parsed?.message === 'string' && parsed.message.trim()) { + message = parsed.message.trim(); + } + } catch (_err) { + // Ignore JSON parse errors, fallback to raw text. + } + } + } catch (_err) { + // Ignore body read errors, keep default message. + } + + throw new Error(message); + } + + // Consume and ignore the body so the fetch can be reused by the connection pool. + try { + await response.text(); + } catch (_err) { + // Swallow body read errors; the deletion already succeeded. + } + } + static async lookupHost(params: { id?: string; hostname?: string }): Promise { const search = new URLSearchParams(); if (params.id) search.set('id', params.id); diff --git a/frontend-modern/src/components/Alerts/ResourceTable.tsx b/frontend-modern/src/components/Alerts/ResourceTable.tsx index b1a2c0c..ea5064f 100644 --- a/frontend-modern/src/components/Alerts/ResourceTable.tsx +++ b/frontend-modern/src/components/Alerts/ResourceTable.tsx @@ -4,6 +4,7 @@ import { StatusBadge } from '@/components/shared/StatusBadge'; import type { Alert } from '@/types/api'; import { Card } from '@/components/shared/Card'; import { SectionHeader } from '@/components/shared/SectionHeader'; +import { ThresholdSlider } from '@/components/Dashboard/ThresholdSlider'; const COLUMN_TOOLTIP_LOOKUP: Record = { 'cpu %': 'Percent CPU utilization allowed before an alert fires.', @@ -48,6 +49,8 @@ const COLUMN_TOOLTIP_LOOKUP: Record = { const OFFLINE_ALERTS_TOOLTIP = 'Toggle default behavior for powered-off or connectivity alerts for this resource type.'; +const SLIDER_METRICS = new Set(['cpu', 'memory', 'disk', 'temperature']); + export interface Resource { id: string; name: string; @@ -79,6 +82,7 @@ export interface Resource { toggleTitleEnabled?: string; toggleTitleDisabled?: string; editable?: boolean; + note?: string; [key: string]: unknown; } @@ -103,6 +107,7 @@ interface ResourceTableProps { resourceId: string, thresholds: Record, defaults: Record, + note: string | undefined, ) => void; onSaveEdit: (resourceId: string) => void; onCancelEdit: () => void; @@ -136,6 +141,8 @@ interface ResourceTableProps { groupHeaderMeta?: Record; factoryDefaults?: Record; onResetDefaults?: () => void; + editingNote: () => string; + setEditingNote: (value: string) => void; } type OfflineState = 'off' | 'warning' | 'critical'; @@ -513,7 +520,7 @@ export function ResourceTable(props: ResourceTableProps) { {/* Global Defaults Row */} - + +

+ +