Skip to content

fix(rmm): correlate Fleet hosts to machines through the FLEET_MDM tool connection first - #2370

Merged
semen-flamingo merged 3 commits into
mainfrom
feature/fleet-host-by-tool-connection
Sep 24, 2026
Merged

semen-flamingo merged 3 commits into
mainfrom
feature/fleet-host-by-tool-connection

Conversation

@semen-flamingo

@semen-flamingo semen-flamingo commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Problem

FleetHostMachineResolver (#2094) maps a Fleet host to a Machine by exact osUuid → hardware_serial → hostname. A machine registered without uuid/serial whose hostname differs from Fleet's lower-cased one (MacBook-Pro-Hryhorii.local vs macbook-pro-hryhorii.local) never correlates. Two visible effects, both seen on QA (test-env, release 1.3.62 / oss 6.36.15):

  1. deviceSoftware / deviceVulnerabilities (Software Management: device-scoped deviceSoftware + deviceVulnerabilities #2351) return an empty connection for such a device — while the frontend's current Software tab shows data, because it reads Fleet directly by the machine's agentToolId (/tools/fleetmdm-server/…/hosts/{id}). Example: MacBook-Pro-Hryhorii.local, ONLINE, Fleet CONNECTED, agentToolId = 29.
  2. devicesCount on softwares / vulnerabilities undercounts the same devices (pre-existing since Software Management. Full API + Interaction with Fleet #2094; fix(rmm): Software & Vulnerabilities lists count devices from one host fetch #2357 kept the resolver). QA: Google Chrome is installed on 13 Fleet hosts, 6–7 of them have a live OpenFrame machine, devicesCount said 5.

Fix

ToolConnection(FLEET_MDM).agentToolId is the Fleet host id — FleetMdmAgentIdTransformer writes String.valueOf(host.getId()) at registration, (tenantId, machineId, toolType) is a unique index, and the frontend already navigates Fleet by it. Both correlation points read it first and keep the identifier match as the fallback:

  • FleetHostMachineResolver.resolve — stage 0: toolConnectionRepository.findByAgentToolIdInAndToolType(hostIds, FLEET_MDM) → machineRepository.findByTenantIdAndMachineIdIn (tenant-scoped, ONLINE/OFFLINE filter as before). The host id is taken from the input host list (no parsing of agentToolId). When several connections point at one host (stale duplicate Machine records after re-enrollment — QA has hosts with 2–4 of them), the newest connectedAt among countable machines wins. Then osUuid → serial → hostname as today. Fixes devicesCount for softwares / vulnerabilities and every other caller.
  • DeviceHostInventoryLoader.findHostId — findByMachineIdAndToolType(machineId, FLEET_MDM) → numeric agentToolId → host id, no Fleet search at all; the search+resolve path stays as fallback for a machine without a connection (or a non-numeric id from a failed transform). Side effect: one Fleet call less per deviceVulnerabilities page.

No schema, config or SDK change. Two repository finders reused, both already existed.

Edge cases checked against live data

  • Stale agentToolId pointing at a host that left the tenant or was deleted: the Fleet fork answers 404 for a host id of another tenant (verified on QA for ids 78, 1, 5, 10, 100 — "Host N was not found in the datastore"), listHostSoftware maps 404 to null → empty inventory, no error.
  • findByAgentToolIdInAndToolType is not tenant-scoped (dev/QA tenants share one Mongo): connections of foreign tenants come back, but findByTenantIdAndMachineIdIn drops their machines, so they can never claim a host or block the identifier fallback.
  • PENDING_DELETION / DELETED duplicates never claim a host in the resolver even when their connection is newer (same ONLINE/OFFLINE rule as before). The device-scoped loader, like the frontend's direct Fleet read, answers for any machine that still has a connection — a PENDING_DELETION record shows its last inventory instead of an empty tab.
  • Non-numeric agentToolId (transform never matched a host) → loader falls back to the search, resolver ignores it.

Verified on QA (test-env, release 1.3.63 / oss 6.36.17, 2026-09-24 17:44 UTC)

  • MacBook-Pro-Hryhorii.local: deviceSoftware 0 → 539 titles, deviceVulnerabilities 0 → 270 CVEs; sort cveCount DESC → Python 75, Word 47, Excel 46, Safari 32, openssl@3 28.
  • Every Fleet-connected device answers now: 16 of 16 (filteredCount > 0), before 6 of 16.
  • softwares → Google Chrome devicesCount 5 → 7; fleet-wide softwares 974 → 1173 titles and vulnerabilities 1010 → 1152 rows with devicesCount > 0 (the newly correlated hosts' software surfaced); top rows count 8 devices.
  • Unchanged where it was already right: vm116194 67 titles, el-romeo 449 titles / 488 CVEs, disjoint cursor pages, NOT_FOUND/404 and BAD_REQUEST/400.
  • Latency: first call after the pod start 8.8 s (host-software cache fill for 19 hosts + 1173-title catalog), then 0.5–1.3 s. api log: only the two expected error responses from the negative cases.

Tests

FleetHostMachineResolverTest +5 (connection beats a case-mismatched hostname; newest of two connections wins; newest connection on a PENDING_DELETION duplicate → the live machine claims; foreign-tenant connection ignored, identifier match still applies; no connections → machine lookup skipped), DeviceHostInventoryLoaderTest +3 (host from the connection with no Fleet search and no resolver call; connected host gone from Fleet → empty inventory; non-numeric agentToolId → search fallback). Regression across the touched classes: 81 green.

Known, not in this PR: tool_connections has no index on agentToolId (the existing findFirstByAgentToolIdOrderByConnectedAtDesc scans too); the collection is small (machines × tools) and the batch lookup runs once per 2 minutes per tenant through CorrelatedHostSoftwareCache, so it is a follow-up index migration, not a blocker.

🤖 Generated with Claude Code

https://claude.ai/code/session_013zAKs7Zs4rWvhKuP3Brmqk

…l connection first

FleetHostMachineResolver matched osUuid -> serial -> hostname exactly; a
machine registered without uuid/serial whose hostname differs from Fleet's
lower-cased one never correlated, so deviceSoftware/deviceVulnerabilities
came back empty and softwares/vulnerabilities undercounted devicesCount.
ToolConnection(FLEET_MDM).agentToolId is the Fleet host id written at
registration, so both the loader and the resolver read it first and keep
the identifier match as a fallback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013zAKs7Zs4rWvhKuP3Brmqk
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🦩 Flamingo Code Review

1 finding(s) — 0 action required · 1 recommended · 0 informational

Mode: advisory · 1 defect(s) outside any rule

Inline comments: 1 new


Need another pass? Commits pushed after this review are not reviewed automatically.

  • Review the new commits — the commits added since this review
  • Review the whole diff again — ignoring what was already reviewed

Prefer typing? Comment @flamingo-review, or @flamingo-review full. To review every push on this pull request, add the flamingo-review-always label.

React 👍/👎 on inline comments to teach the reviewer.

Started 2026-09-24 14:58 UTC · updated 2026-09-24 14:59 UTC · workflow run

semen-flamingo and others added 2 commits September 24, 2026 17:05
…rsing agentToolId

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013zAKs7Zs4rWvhKuP3Brmqk
…tion duplicate, foreign tenant, host gone from Fleet

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013zAKs7Zs4rWvhKuP3Brmqk
@semen-flamingo
semen-flamingo merged commit ffddc72 into main Sep 24, 2026
12 of 13 checks passed
@semen-flamingo
semen-flamingo deleted the feature/fleet-host-by-tool-connection branch September 24, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants