Skip to content

Custom host vitals: schema + CRUD (backend)#48706

Open
nulmete wants to merge 4 commits into
44954-custom-host-vitalsfrom
48559-custom-host-vitals-crud
Open

Custom host vitals: schema + CRUD (backend)#48706
nulmete wants to merge 4 commits into
44954-custom-host-vitalsfrom
48559-custom-host-vitals-crud

Conversation

@nulmete

@nulmete nulmete commented Jul 3, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #48559

Adds the schema (global definitions + per-host values) and the CRUD API: create/list/rename/delete definitions, set a per-host value, the host-details embed, and the related activities.

Checklist for submitter

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

Will be added in the feature branch directly.

Testing

  • Added/updated automated tests

  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • New Features

    • Added support for custom host vitals, including creating, renaming, deleting, listing, and setting values per host.
    • Host details now show any custom vitals assigned to that machine.
    • Activity history now records custom host vital changes.
  • Bug Fixes

    • Improved access controls so only appropriately authorized users can view or manage custom host vitals.
    • Added search and pagination support when browsing custom host vitals.

@nulmete

nulmete commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds a "custom host vitals" feature: globally-defined, per-host key/value fields. It introduces authorization rules, new custom_host_vitals and host_custom_host_vitals MySQL tables via migration, datastore CRUD methods, fleet types with name validation, activity types, REST API request/response types, service-layer endpoints for CRUD and setting per-host values (with authorization and activity logging), new HTTP routes, host-detail integration exposing per-host vitals, mock updates, and accompanying unit/integration tests.

Changes

Area Change
Authorization New custom_host_vital rego rules and tests for global/team read/write access
Schema/Migration New custom_host_vitals and host_custom_host_vitals tables with cascade FKs
Datastore CRUD methods, host value set/get, batch fetch by IDs
Fleet types CustomHostVital, HostCustomHostVital, name validation, activity types, API request/response types, interface extensions
Service List/Create/Update/Delete endpoints, host value setter with two-stage authorization, activity recording
Routing New REST endpoints under /api/_version_/fleet
Host details HostDetail.CustomHostVitals populated via datastore lookup
Mocks/tests Datastore/service mocks, unit tests, migration tests, integration CRUD test

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Service
  participant Authz
  participant Datastore
  Client->>Service: CreateCustomHostVital(name)
  Service->>Authz: authorize write
  Service->>Service: ValidateCustomHostVitalName(name)
  Service->>Datastore: CreateCustomHostVital(name)
  Datastore-->>Service: CustomHostVital
  Service->>Datastore: NewActivity(CreatedCustomHostVital)
  Service-->>Client: *CustomHostVital
Loading

Related issues: #48559 (Backend: custom host vitals — migrations, CRUD endpoints, host-value setter, authorization)

Suggested labels: backend, enhancement, needs review

Suggested reviewers: fleetdm backend maintainers familiar with authz policy and MySQL datastore layers

🐰 A vital new field, host by host,
Migrations laid down, no data lost,
Rego guards the read and write,
Activities logged with each new byte,
Custom vitals now take flight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Core schema and CRUD are present, but required script/profile validation, per-host expansion, delete blocking, and GitOps support are not in this slice. Implement the missing $FLEET_HOST_VITAL_ validation/expansion paths, in-use delete protection, and GitOps support, and include reviewable activity docs.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed All changed files are directly related to custom host vitals schema, API, auth, datastore, tests, and host-detail plumbing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title is concise and accurately summarizes the main backend change.
Description check ✅ Passed It includes the related issue, a clear summary, and testing notes, though several optional checklist items are left unchecked.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 48559-custom-host-vitals-crud

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
server/service/hosts_test.go (1)

94-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate mock stub assignments.

ConditionalAccessBypassedAtFunc and GetHostCustomHostVitalsFunc are each assigned twice in this block (lines 94-96 duplicated at 103-105, and 97-99 duplicated at 106-108). Harmless but likely a rebase artifact.

🧹 Proposed cleanup
 	ds.ConditionalAccessBypassedAtFunc = func(ctx context.Context, hostID uint) (*time.Time, error) {
 		return nil, nil
 	}
 	ds.GetHostCustomHostVitalsFunc = func(ctx context.Context, hostID uint) ([]fleet.HostCustomHostVital, error) {
 		return nil, nil
 	}
 	ds.IsHostDiskEncryptionKeyArchivedFunc = func(ctx context.Context, hostID uint) (bool, error) {
 		return false, nil
 	}
-	ds.ConditionalAccessBypassedAtFunc = func(ctx context.Context, hostID uint) (*time.Time, error) {
-		return nil, nil
-	}
-	ds.GetHostCustomHostVitalsFunc = func(ctx context.Context, hostID uint) ([]fleet.HostCustomHostVital, error) {
-		return nil, nil
-	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/service/hosts_test.go` around lines 94 - 108, Remove the duplicated
mock stub assignments in the test setup so each function is only configured
once; in the host test block, keep a single assignment for
ConditionalAccessBypassedAtFunc and a single assignment for
GetHostCustomHostVitalsFunc, and delete the repeated copies introduced after
IsHostDiskEncryptionKeyArchivedFunc.
server/service/custom_host_vitals_test.go (1)

96-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for team-scoped success and vital-not-found paths.

Two gaps in TestSetHostCustomHostVitalValueAuth:

  • The comment claims "team maintainers/admins (of the host's team) can set it," but HostLiteFunc always returns a host with no TeamID, so no team-scoped success case is actually exercised — only failing cases are covered.
  • No case exercises customHostVitalByID returning NotFound (e.g., GetCustomHostVitalsFunc returning an empty slice), even though that's an explicit guard in SetHostCustomHostVitalValue.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/service/custom_host_vitals_test.go` around lines 96 - 130,
`TestSetHostCustomHostVitalValueAuth` only covers global roles and negative team
observer behavior, so it never verifies the team-scoped allow path or the
not-found guard. Update the setup around `HostLiteFunc` in
`SetHostCustomHostVitalValue` tests to return a host with a matching `TeamID`,
then add a case for a team maintainer/admin on that team that succeeds. Also add
a separate case where `GetCustomHostVitalsFunc` returns no vitals so
`customHostVitalByID` in `SetHostCustomHostVitalValue` hits the `NotFound` path
and the test asserts that error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/service/custom_host_vitals_test.go`:
- Around line 96-130: `TestSetHostCustomHostVitalValueAuth` only covers global
roles and negative team observer behavior, so it never verifies the team-scoped
allow path or the not-found guard. Update the setup around `HostLiteFunc` in
`SetHostCustomHostVitalValue` tests to return a host with a matching `TeamID`,
then add a case for a team maintainer/admin on that team that succeeds. Also add
a separate case where `GetCustomHostVitalsFunc` returns no vitals so
`customHostVitalByID` in `SetHostCustomHostVitalValue` hits the `NotFound` path
and the test asserts that error.

In `@server/service/hosts_test.go`:
- Around line 94-108: Remove the duplicated mock stub assignments in the test
setup so each function is only configured once; in the host test block, keep a
single assignment for ConditionalAccessBypassedAtFunc and a single assignment
for GetHostCustomHostVitalsFunc, and delete the repeated copies introduced after
IsHostDiskEncryptionKeyArchivedFunc.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 49b5872e-efb0-4a81-a57f-43668759b083

📥 Commits

Reviewing files that changed from the base of the PR and between 6525921 and 324e43c.

⛔ Files ignored due to path filters (1)
  • docs/Contributing/reference/audit-logs.md is excluded by !**/*.md
📒 Files selected for processing (24)
  • server/authz/policy.rego
  • server/authz/policy_test.go
  • server/datastore/mysql/custom_host_vitals.go
  • server/datastore/mysql/custom_host_vitals_test.go
  • server/datastore/mysql/migrations/tables/20260702175822_CustomHostVitals.go
  • server/datastore/mysql/migrations/tables/20260702175822_CustomHostVitals_test.go
  • server/datastore/mysql/schema.sql
  • server/fleet/activities.go
  • server/fleet/api_custom_host_vitals.go
  • server/fleet/custom_host_vitals.go
  • server/fleet/datastore.go
  • server/fleet/fleet_vars.go
  • server/fleet/hosts.go
  • server/fleet/service.go
  • server/mock/datastore_mock.go
  • server/mock/service/service_mock.go
  • server/service/custom_host_vitals.go
  • server/service/custom_host_vitals_test.go
  • server/service/devices_endpoint_test.go
  • server/service/handler.go
  • server/service/hosts.go
  • server/service/hosts_test.go
  • server/service/integration_custom_host_vitals_test.go
  • server/service/testing_utils_test.go

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.95035% with 65 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (44954-custom-host-vitals@6525921). Learn more about missing BASE report.

Files with missing lines Patch % Lines
server/service/custom_host_vitals.go 74.19% 16 Missing and 16 partials ⚠️
server/datastore/mysql/custom_host_vitals.go 76.28% 14 Missing and 9 partials ⚠️
...grations/tables/20260702175822_CustomHostVitals.go 83.78% 4 Missing and 2 partials ⚠️
server/fleet/custom_host_vitals.go 80.00% 1 Missing and 1 partial ⚠️
server/service/hosts.go 50.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@                     Coverage Diff                     @@
##             44954-custom-host-vitals   #48706   +/-   ##
===========================================================
  Coverage                            ?   68.00%           
===========================================================
  Files                               ?     3682           
  Lines                               ?   233874           
  Branches                            ?    12385           
===========================================================
  Hits                                ?   159049           
  Misses                              ?    60496           
  Partials                            ?    14329           
Flag Coverage Δ
backend 69.66% <76.95%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nulmete nulmete marked this pull request as ready for review July 6, 2026 13:15
@nulmete nulmete requested review from a team and rachaelshaw as code owners July 6, 2026 13:15

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

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