Follow-up to the #1341 dual-source fix. When the Proxmox API identity
wins the FSID dedup, proactively clear any usage alert still active under
this agent's own pool IDs so a pre-fix duplicate clears on the next agent
report rather than aging out over the ~24h stale-alert window. Agent-only
clusters (where the agent identity is the winner) are untouched.
Fixes#1341 (reopened)
When the same Ceph cluster is reported by both the Proxmox API poller
(instance "pve5") and a Pulse host-agent (instance "agent:pve5"), it
landed in state twice under two pool-ID namespaces
("pve5-ceph-pool-X" vs "agent:pve5-ceph-pool-X"). The frontend collapsed
them via an FSID dedup whose winner was chosen by a fluctuating
completeness score, but alert evaluation ran on the raw, un-deduped list
(and ce1607694 added a third check on the raw agent cluster). So:
- the threshold UI row identity flipped between the two pool IDs as the
dedup winner oscillated, making a saved per-pool override appear to
revert between the custom value and the storage default; and
- both pool IDs were alert-checked under independent lifecycles,
producing duplicate, flapping active alerts.
Make alert evaluation and the frontend consume the same deterministic
FSID-deduplicated Ceph view (DedupeCephClusters), preferring the
authoritative non-agent identity so a pool has exactly one ID. Route the
agent-report and mock alert paths through GetDedupedCephClusters, and
honor the legacy "agent:"-prefixed override key for the surviving pool so
existing thresholds keep firing without manual re-entry. Retires the
ce1607694 agent-only check path.
Centralize the QEMU guest-agent MemAvailable fallback on the 5.1 branch and record the direct guest-agent value in diagnostic snapshots.
Add regression coverage for the issue #1319 saturated Linux memory payload and Windows fsinfo volume payload.
Refs #1319
Fixes#1341
When a Pulse host-agent reports Ceph data, ApplyHostReport upserts the
cluster into state but only the Proxmox-API polling path ran
cephPoolAlertStorageTargets. Users with agent-reported Ceph (instance
prefix "agent:hostname") saved per-pool overrides under
agent-prefixed IDs that the alert manager never evaluated. The
threshold appeared to save (and showed Custom in the UI), but the
polling cycle was checking a different storage ID, so the alert
silently stayed dormant. Run CheckStorage for each pool right after
the agent upsert so the override key actually drives evaluation.
Refs #1341
UpdateAlertConfig used to log SaveAlertConfig failures and still tell
the client "saved successfully", leaving the in-memory state with the
new override but the on-disk file untouched. On the next config reload
or process restart, the override silently vanished and the user saw
their threshold "revert" with no surfaced error. Return HTTP 500 with
the persistence error so the frontend can show a real save-failed
toast instead of false confidence.
Refs #1341
buildAlertsDiagnostic previously emitted only boolean flags (legacy
thresholds, missing cooldown/grouping window). Override keys and their
trigger values were absent, so triaging a support case where the user
suspects an override mismatch required asking them to paste
data/alerts.json from inside their container. Add an Overrides slice
that names each persisted key with its thresholds and disabled flags.
Sanitize mode in the frontend redacts the keys to override-N while
keeping thresholds visible, so a public export still shows the alert
shape without leaking instance names that may be hostnames.
Refs #1341
The 5.1.32 fix in 6f3bea32f only tested the storage default path for
synthetic Ceph pool entries. Reporter on #1341 sets a 50% per-pool
override on a pool at ~61% usage and still doesn't get an alert; lock
the override path against future regressions so this remains a config
question rather than a code regression.
Ensure the dashboard guest table expands to 100% of the container width on large screens (especially under the Proxmox overview tab), while retaining the minimum width calculation to prevent horizontal collapses.
Fixes#1480
Normalize diagnostics collection fields to empty arrays before encoding and harden the sanitized GitHub export path against null arrays so empty v5 installs can still produce issue attachments.
Refs #1454
Render the maintenance installer URL into the generated update helper so it does not depend on installer-only shell functions after installation. Add a smoke test that executes the generated helper with fake curl and bash to preserve source-build forwarding.\n\nRefs #1454
Keeps the release/5.1 frontend lockfile above the patched floor for GHSA-qx2v-qp2m-jg93 and aligned with the default-branch Dependabot fix.
Refs Dependabot alert #83.
Pulse is a single-maintainer project and does not accept unsolicited
external pull requests. README, CONTRIBUTING, and a new
PULL_REQUEST_TEMPLATE now state this directly so contributors hit the
policy before investing time in code, and so PRs opened in error point
to issues and discussions as the correct intake.
CONTRIBUTING is rewritten end-to-end around the new policy: how to
file bugs, feature requests, support questions, and security reports;
where to look for context (README, ARCHITECTURE, docs/); and the
maintainer-direction carve-out for PRs explicitly requested against
tracked issues.
shouldNotifyAfterCooldown previously returned true on every call when
Schedule.Cooldown was 0 or negative, which the alert evaluation loop
runs on every metric tick. With cooldown disabled, an active alert was
re-notified on each tick.
The UI labels cooldown=0 as "Disabled," so the intuitive contract is
"do not re-notify," not "re-notify continuously." Treat <=0 as
"first-time only": fire the initial notification, then suppress
subsequent re-notifications until the alert clears or the cooldown is
configured to a positive value. Level escalation re-notifications
remain handled at the call site and are unaffected.
Tests cover all three branches: first-time fire with cooldown=0,
re-notification suppression with cooldown=0 (named regression guard
for #1444), and the same behavior for negative values.
The modal had no close path when isComplete() was false: the X button
was Show-gated on isComplete(), there was no Escape handler, and the
backdrop had no onClick. So if the SSE stream dropped, the polling
fallback failed, or the update process crashed before writing a
terminal status, the modal stayed open with a black backdrop covering
the page and no way to dismiss it except a hard browser refresh — the
"page is blacked out and you can't press anything" symptom.
Make the close path always available:
- The X button in the header is no longer Show-gated. Its tooltip
and aria-label adapt to clarify that closing during an active
update only hides the modal — the update keeps running.
- Escape on the document closes the modal while it is open.
- Clicking on the backdrop (and only the backdrop, not the modal
body) closes the modal.
The actual update process is server-side and unaffected: closing
just unmounts the modal's local SSE/polling. GlobalUpdateProgressWatcher
keeps polling /api/updates/status independently and will surface
completion via the existing reload path or via the Updates settings
page.
Frontend type-check passes and the 447-test vitest suite is green.
Pulse agents derive their identity from /etc/machine-id by default. In
Docker containers (especially nested in LXCs), /etc/machine-id is not
guaranteed stable across container recreation: a fresh image instance
gets a new machine-id, and the resulting AgentID drift causes the
server to reject reports with 401 because the API token is bound to
the original AgentID via the bound_agent_id token-metadata check
(internal/api/router.go:1448-1458). Refs #1447.
Add a --agent-id-file (and PULSE_AGENT_ID_FILE env var) flag that:
1. Reads the persisted AgentID from the file on start, when present,
and short-circuits machine-id detection. The user mounts the file
as a Docker volume (e.g. -v pulse-agent-id:/var/lib/pulse-agent)
so it survives container recreation.
2. On first start (or when the file is missing/empty), the existing
machine-id derivation runs and the resolved ID is written to the
file atomically (tmp + rename, 0600 perms, parent dir created).
Subsequent restarts of the container — even after `docker rm -f` and
a fresh `docker run` — read the same ID from the volume and the
server keeps recognising the agent.
Default is no flag set, which preserves the current
/etc/machine-id-derived behaviour for non-containerized installs.