Skip to content

Add GitOps support for custom host vital definitions#49277

Merged
nulmete merged 5 commits into
44954-custom-host-vitalsfrom
48558-gitops-custom-host-vitals-apply
Jul 14, 2026
Merged

Add GitOps support for custom host vital definitions#49277
nulmete merged 5 commits into
44954-custom-host-vitalsfrom
48558-gitops-custom-host-vitals-apply

Conversation

@nulmete

@nulmete nulmete commented Jul 14, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #48558

  • Adds a custom_host_vitals GitOps key, letting fleetctl gitops create, update, and remove custom host vital definitions.
  • Introduces a new PUT /spec/custom_host_vitals server endpoint that reconciles the full set of definitions in one call
  • Extends fleetctl generate-gitops to emit existing vitals so a GitOps repo can capture and round-trip this configuration.

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.

Testing

  • Added/updated automated tests

  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • New Features
    • Added GitOps support for a custom_host_vitals section in global YAML to declare, reconcile, and report custom host vitals.
    • Added a custom host vitals API that validates names, supports dry-run, and provides create/delete feedback.
    • Supports clearing vitals by explicitly providing an empty list (or null), while omitting the key leaves current vitals unchanged.
  • Bug Fixes
    • Prevents deleting vitals that are still referenced by scripts or other resources.
    • Preserves existing vital IDs when vital definitions are unchanged and rejects invalid/duplicate vital names.

Adds a declarative `custom_host_vitals:` GitOps key so fleetctl can
create, update, and remove custom host vital definitions, and emits
existing definitions via `fleetctl generate-gitops` for round-tripping.
@nulmete

nulmete commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

make mock wasn't re-run after moving the interface method's position
in server/fleet/datastore.go, so the committed mock was stale.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds global GitOps support for custom_host_vitals. YAML parsing validates names, tracks key presence, and supports clearing. GitOps apply reconciles additions and deletions with dry-run behavior and deletion protection. New API, service, datastore, mock, and integration-test coverage supports upserts. generate-gitops now lists and emits custom vital names in global default.yml.

Possibly related issues

Possibly related PRs

  • fleetdm/fleet#48706 — Provides the backend custom host vital API foundation used by this GitOps upsert path.
  • fleetdm/fleet#48821 — Provides delete-protection behavior reused by custom host vital reconciliation.
  • fleetdm/fleet#49240 — Updates the delete-protection logic used by the new datastore upsert operation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy #48558 by adding declarative custom_host_vitals apply support, generate-gitops output, and round-trip tests.
Out of Scope Changes check ✅ Passed The diff appears focused on custom_host_vitals GitOps support, with no unrelated or out-of-scope changes evident.
Title check ✅ Passed The title is concise and clearly summarizes the main change: adding GitOps support for custom host vital definitions.
Description check ✅ Passed It includes the required related issue, summary, and testing sections, and the remaining checklist items are plausibly deferred or optional.
✨ 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 48558-gitops-custom-host-vitals-apply

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.

Actionable comments posted: 1

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

3082-3082: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Skip the API request when there are no changes to apply.

If toDelete and toAdd are both empty, the GitOps definitions already match the server's state perfectly. You can safely early-return here to avoid making an unnecessary PUT request that overwrites the vitals with identical data.

♻️ Proposed refactor
 		}
 	}
 
+	if len(toDelete) == 0 && len(toAdd) == 0 {
+		return nil
+	}
+
 	if dryRun {
 		if len(toDelete) > 0 {
🤖 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/client.go` at line 3082, Update the flow around the dryRun
condition to return before the API request when both toDelete and toAdd are
empty, preserving existing behavior when either list contains changes. Ensure
the early return occurs before the PUT request and does not alter dry-run
handling.
pkg/spec/gitops.go (1)

1106-1113: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Check for duplicate names during parsing.

Currently, if the GitOps YAML contains duplicate custom host vital names, the parser accepts them, which will result in an unhelpful database constraint error when sent to the server. You can catch this early and provide a clearer error message by tracking seen names.

♻️ Proposed refactor
 	// Validate unknown keys in the custom_host_vitals section.
 	multiError = multierror.Append(multiError, validateRawKeys(raw, reflect.TypeFor[[]GitOpsCustomHostVital](), filePath, []string{"custom_host_vitals"})...)
 
+	seenNames := make(map[string]bool)
 	for _, v := range vitals {
 		if err := fleet.ValidateCustomHostVitalName(v.Name); err != nil {
 			multiError = multierror.Append(multiError, fmt.Errorf("'custom_host_vitals': %w", err))
 			continue
 		}
+		if seenNames[v.Name] {
+			multiError = multierror.Append(multiError, fmt.Errorf("'custom_host_vitals': duplicate custom host vital %q", v.Name))
+			continue
+		}
+		seenNames[v.Name] = true
 		result.CustomHostVitals = append(result.CustomHostVitals, fleet.CustomHostVital{Name: v.Name})
 	}
🤖 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 `@pkg/spec/gitops.go` around lines 1106 - 1113, Update the custom host vitals
parsing loop to track names already encountered and append a clear validation
error when a duplicate is found. Perform the duplicate check alongside
fleet.ValidateCustomHostVitalName before appending to result.CustomHostVitals,
and only append unique valid vitals while preserving existing multiError
handling.
🤖 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.

Inline comments:
In `@server/service/custom_host_vitals.go`:
- Around line 248-257: Update the duplicate detection in the custom host vital
validation loop around seen to use utf8mb4_unicode_ci-equivalent comparison or
normalization, matching the database unique index. Ensure collation-equivalent
names, including case-only differences, are rejected before insertion while
preserving the existing invalid-argument error behavior.

---

Nitpick comments:
In `@pkg/spec/gitops.go`:
- Around line 1106-1113: Update the custom host vitals parsing loop to track
names already encountered and append a clear validation error when a duplicate
is found. Perform the duplicate check alongside
fleet.ValidateCustomHostVitalName before appending to result.CustomHostVitals,
and only append unique valid vitals while preserving existing multiError
handling.

In `@server/service/client.go`:
- Line 3082: Update the flow around the dryRun condition to return before the
API request when both toDelete and toAdd are empty, preserving existing behavior
when either list contains changes. Ensure the early return occurs before the PUT
request and does not alter dry-run handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e86b262e-f6f2-448d-b27a-818fedda83f7

📥 Commits

Reviewing files that changed from the base of the PR and between 65dfc6a and 12eac61.

📒 Files selected for processing (20)
  • cmd/fleetctl/fleetctl/generate_gitops.go
  • cmd/fleetctl/fleetctl/generate_gitops_test.go
  • cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_free/default.yml
  • cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/default.yml
  • cmd/fleetctl/fleetctl/testing_utils/testing_utils.go
  • cmd/fleetctl/integrationtest/gitops/gitops_integration_test.go
  • pkg/spec/gitops.go
  • pkg/spec/gitops_test.go
  • server/datastore/mysql/custom_host_vitals.go
  • server/datastore/mysql/custom_host_vitals_test.go
  • server/fleet/api_custom_host_vitals.go
  • server/fleet/datastore.go
  • server/fleet/service.go
  • server/mock/datastore_mock.go
  • server/mock/service/service_mock.go
  • server/service/client.go
  • server/service/client_custom_host_vitals.go
  • server/service/custom_host_vitals.go
  • server/service/custom_host_vitals_test.go
  • server/service/handler.go

Comment thread server/service/custom_host_vitals.go Outdated
@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
server/datastore/mysql/custom_host_vitals.go 76.74% 5 Missing and 5 partials ⚠️
cmd/fleetctl/fleetctl/generate_gitops.go 75.00% 4 Missing and 2 partials ⚠️
server/service/custom_host_vitals.go 89.36% 3 Missing and 2 partials ⚠️
server/service/client.go 90.00% 2 Missing and 2 partials ⚠️
server/service/client_custom_host_vitals.go 83.33% 2 Missing and 2 partials ⚠️
pkg/spec/gitops.go 90.90% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@                     Coverage Diff                     @@
##             44954-custom-host-vitals   #49277   +/-   ##
===========================================================
  Coverage                            ?   68.02%           
===========================================================
  Files                               ?     3684           
  Lines                               ?   234523           
  Branches                            ?    12385           
===========================================================
  Hits                                ?   159532           
  Misses                              ?    60617           
  Partials                            ?    14374           
Flag Coverage Δ
backend 69.68% <84.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.

doGitOpsCustomHostVitals unconditionally fetched existing definitions
over the network, which ran before org_settings.integrations/CA
validation and broke TestGitOpsErrors (a client with no auth token,
used to test that local-only validation fails before any network
call). Move the call to the end of the global config branch, after
all local validation and the EULA network call.
custom_host_vitals.name is unique under utf8mb4_unicode_ci
(case-insensitive) collation, but the service-level duplicate check
used exact string comparison, so case-only duplicates would pass
validation and surface as a raw DB error at insert time instead.
@nulmete

nulmete commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nulmete
nulmete marked this pull request as ready for review July 14, 2026 19:06
@nulmete
nulmete requested a review from a team as a code owner July 14, 2026 19:06

@lucasmrod lucasmrod left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM! Left two questions/comments.

Comment thread pkg/spec/gitops.go Outdated
// rather than a directive to clear settings. settings keys were handled above.
if topKey == "name" || topKey == "labels" || topKey == "software" || topKey == "settings" || topKey == "org_settings" {
// "labels", "software", and "custom_host_vitals" are special cases where omitting may be a
// no-op (based on exception settings), rather than a directive to clear settings. settings

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are custom host vitals in the exception settings for GitOps?

@nulmete nulmete Jul 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No, I was wrong and copy-pasted from the labels/software case... custom host vitals have no exceptions.
It's excluded from the generic null-default loop because it needs its own presence tracking, not because of any exception mechanism (omitting the key always means clear-all).
I updated the comment to reflect that.

Comment thread server/service/client.go
…st vitals

- pkg/spec/gitops.go: custom host vitals have no GitOps exception
  setting (unlike labels/software) -- correct the comment that
  implied otherwise; it's excluded from the generic null-default
  loop only because it needs its own presence tracking.
- server/service/client.go: dry-run returned early without ever
  calling the server, so server-side validation (name format,
  collation-aware duplicate check) was skipped on --dry-run and
  would only surface on a real run. Send the request with DryRun
  set so the server validates without persisting.
@nulmete
nulmete requested a review from lucasmrod July 14, 2026 21:12
nulmete added a commit that referenced this pull request Jul 14, 2026
…tals section

- Add "Upsert custom host vitals" to api-for-contributors.md,
  alongside the analogous PUT /spec/secret_variables entry, for the
  new declarative GitOps spec endpoint (added in #49277).
- Fix nit from review: name parameter description referenced the
  wrong prefix (FLEET_SECRET_ instead of FLEET_HOST_VITAL_).
- Remove a copy-pasted "value" parameter from Create custom host
  vital (the endpoint only accepts name; per-host values are set via
  a separate endpoint).
- Fix invalid JSON (trailing commas) in two examples.
- Fix "Update host's custom host vital value" missing the
  /api/v1/fleet path prefix used by every other endpoint in the doc.
@nulmete
nulmete merged commit f49b84a into 44954-custom-host-vitals Jul 14, 2026
38 checks passed
@nulmete
nulmete deleted the 48558-gitops-custom-host-vitals-apply branch July 14, 2026 23:03
@coderabbitai coderabbitai Bot mentioned this pull request Jul 15, 2026
3 tasks
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