Change self-service categories GitOps to not require dedicated key#47439
Conversation
Removes obsolete tests for the client-side/spec category logic that was removed (spec derivation, generate-gitops top-level emission) and reworks the gitops reconcile test. Committed separately so the production diff for the server-side category handling can be reviewed on its own.
Derive each fleet's self-service categories from the categories referenced by its software rather than a top-level key. Categories are created server-side in the software and VPP batch endpoints (including FMA manifest defaults, which are only known after manifest resolution), and the GitOps client prunes categories no software references — skipped when software is excepted from GitOps, while an omitted software section still prunes (consistent with software being deleted). - batch endpoints collect/validate/create referenced categories and return them - GitOps client unions the installer and VPP categories and deletes the rest - TranslateLegacySoftwareCategoryNames is now case-insensitive - remove the old client-side reconcile and spec-level derivation
A fleet-maintained app's categories key now parses into an optjson.Slice so the server can tell an omitted key (use the manifest's default categories) from a present-but-null/empty one (zero categories). Categories on MaintainedAppSpec, SoftwarePackageSpec, and SoftwareInstallerPayload are optjson.Slice tagged omitzero so the set/unset distinction survives the gitops client's internal team-spec re-marshal — omitempty is a no-op on struct types and would collapse omitted into null. softwareInstallerPayloadFromSlug checks Categories.Set. Adds parse coverage (pkg/spec) and a batch round-trip case for omitted vs explicit-empty FMA categories (integration_enterprise_test).
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #47439 +/- ##
==========================================
+ Coverage 67.19% 67.22% +0.02%
==========================================
Files 3068 3073 +5
Lines 226816 227552 +736
Branches 11867 11867
==========================================
+ Hits 152417 152968 +551
- Misses 60658 60756 +98
- Partials 13741 13828 +87
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThis PR migrates self-service software category reconciliation from a centralized fleetctl GitOps generation phase to per-operation collection within batch service methods. The schema for GitOps YAML is simplified by removing the Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
ee/server/service/software_installers.go (1)
2360-2368: 💤 Low valueCategory validation occurs before batch is started, but errors may be silent for individual payloads.
The
trimAndValidateCategoriescall validates categories per payload, but the error returned doesn't identify which payload failed. Consider including the payload index or URL in the error context for easier debugging.- if err := trimAndValidateCategories(ctx, payload.Categories.Value); err != nil { - return "", ctxerr.Wrap(ctx, err, "validating software categories") + if err := trimAndValidateCategories(ctx, payload.Categories.Value); err != nil { + return "", ctxerr.Wrap(ctx, err, fmt.Sprintf("validating software categories for payload %d (url: %s)", i, payload.URL)) }🤖 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 `@ee/server/service/software_installers.go` around lines 2360 - 2368, The validation call trimAndValidateCategories(payload.Categories.Value) returns errors without identifying which payload failed; update the loop that calls trimAndValidateCategories (the code handling payload and payload.Categories.Value before calling svc.batchAddSelfServiceCategories) to wrap or augment the returned error with the payload's index and/or payload URL (e.g., include payloadIndex and payload.URL) so the ctxerr.Wrap or returned error includes that context; keep using trimAndValidateCategories and batchAddSelfServiceCategories but ensure any validation error references the specific payload (index or URL) for easier debugging.
🤖 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 `@ee/server/service/vpp.go`:
- Around line 304-308: The code calls svc.batchAddSelfServiceCategories(...) too
early and persists categories before subsequent platform/token/asset/config
validations complete; move the call to batchAddSelfServiceCategories (or the DB
persistence it performs) to after the later validation steps in the enclosing
method (the platform/token/asset/config checks), or implement a transactional
rollback/cleanup when those validations fail (e.g., track created category IDs
from batchAddSelfServiceCategories and delete them on error). Ensure you
reference and update the logic around batchAddSelfServiceCategories, the
validation blocks (platform/token/asset/config checks), and the error paths so
categories are only persisted on overall success or removed on failure.
In `@pkg/spec/gitops.go`:
- Around line 312-316: Add a parse-only deprecated alias field to the existing
Software struct so existing GitOps YAML with software.self_service_categories
doesn't hard-fail: add a new field SelfServiceCategories of type json.RawMessage
(or interface{}) to type Software with tags
json:"self_service_categories,omitempty" and
yaml:"self_service_categories,omitempty" and a comment like "// Deprecated:
parse-only alias for backward compatibility" and ensure parseSoftware continues
to validate against Software but any code paths that produce/derive categories
ignore this field (do not use it in upstream logic).
In `@server/service/client.go`:
- Around line 1098-1135: The categories keep-set is being stored under the
original YAML name (tmName) but the rest of the batch uses the resolved name
(currentTeamName), causing mismatches during pruning; update both places where
categories are appended (after ApplyTeamSoftwareInstallers and after
ApplyTeamAppStoreAppsAssociation) to use currentTeamName as the map key
(categoriesByTeam[currentTeamName] = append(...)) so the
deleteUnusedSelfServiceCategories call that iterates teamIDsByName finds the
correct keep-set; references: categoriesByTeam, tmName, currentTeamName,
ApplyTeamSoftwareInstallers, ApplyTeamAppStoreAppsAssociation,
deleteUnusedSelfServiceCategories, teamIDsByName.
---
Nitpick comments:
In `@ee/server/service/software_installers.go`:
- Around line 2360-2368: The validation call
trimAndValidateCategories(payload.Categories.Value) returns errors without
identifying which payload failed; update the loop that calls
trimAndValidateCategories (the code handling payload and
payload.Categories.Value before calling svc.batchAddSelfServiceCategories) to
wrap or augment the returned error with the payload's index and/or payload URL
(e.g., include payloadIndex and payload.URL) so the ctxerr.Wrap or returned
error includes that context; keep using trimAndValidateCategories and
batchAddSelfServiceCategories but ensure any validation error references the
specific payload (index or URL) for easier debugging.
🪄 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: cf272350-67a2-40d8-aaae-41cdf27e5f73
📒 Files selected for processing (26)
cmd/fleetctl/fleetctl/generate_gitops.gocmd/fleetctl/fleetctl/generate_gitops_test.gocmd/fleetctl/fleetctl/get.gocmd/fleetctl/fleetctl/gitops_test.gocmd/fleetctl/fleetctl/testing_utils/testing_utils.gocmd/fleetctl/fleetctl/testing_utils_test.goee/server/service/categories.goee/server/service/software_installers.goee/server/service/software_installers_test.goee/server/service/vpp.goee/server/service/vpp_test.gopkg/spec/gitops.gopkg/spec/gitops_test.goserver/fleet/scripts.goserver/fleet/service.goserver/fleet/software.goserver/fleet/software_installer.goserver/mock/service/service_mock.goserver/service/client.goserver/service/client_software.goserver/service/client_teams.goserver/service/integration_enterprise_test.goserver/service/integration_mdm_test.goserver/service/software_installers.goserver/service/software_installers_test.gotools/cloner-check/generated_files/teamconfig.txt
💤 Files with no reviewable changes (2)
- cmd/fleetctl/fleetctl/generate_gitops.go
- cmd/fleetctl/fleetctl/generate_gitops_test.go
| // since we actually add categories here, it's possible they can stay orphaned until the next run if the run fails | ||
| categories, err := svc.batchAddSelfServiceCategories(ctx, teamID, categoryNames, dryRun) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } |
There was a problem hiding this comment.
Delay category creation until the batch has passed the later validations.
Lines 304-308 persist categories before the method can still fail on platform/token/asset/config checks later in the function. That leaves categories in the database with no associated software, and the client-side prune only runs after a successful GitOps apply. Please move category creation after the failure-prone validation steps, or add rollback/cleanup on error.
🤖 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 `@ee/server/service/vpp.go` around lines 304 - 308, The code calls
svc.batchAddSelfServiceCategories(...) too early and persists categories before
subsequent platform/token/asset/config validations complete; move the call to
batchAddSelfServiceCategories (or the DB persistence it performs) to after the
later validation steps in the enclosing method (the platform/token/asset/config
checks), or implement a transactional rollback/cleanup when those validations
fail (e.g., track created category IDs from batchAddSelfServiceCategories and
delete them on error). Ensure you reference and update the logic around
batchAddSelfServiceCategories, the validation blocks
(platform/token/asset/config checks), and the error paths so categories are only
persisted on overall success or removed on failure.
There was a problem hiding this comment.
I think this is acceptable, since it just leaves orphaned rows that will be cleaned up (or used) in the next run (if your non-dry gitops run failed you will probably want to run another one to fix it anyway). Fixing this would take a bit more than I would like to include in this PR. ~50-60 extra lines in that function according to claude.
Let me know if I should address this though.
| type Software struct { | ||
| Packages []SoftwarePackage `json:"packages"` | ||
| AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"` | ||
| FleetMaintainedApps []fleet.MaintainedAppSpec `json:"fleet_maintained_apps"` | ||
| SelfServiceCategories optjson.Slice[string] `json:"self_service_categories"` | ||
| Packages []SoftwarePackage `json:"packages"` | ||
| AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"` | ||
| FleetMaintainedApps []fleet.MaintainedAppSpec `json:"fleet_maintained_apps"` | ||
| } |
There was a problem hiding this comment.
Keep self_service_categories as a deprecated parse-only alias.
Removing the field from Software turns existing GitOps YAML that still includes software.self_service_categories: into a hard parse failure, because parseSoftware still validates raw keys against this struct before the new server-side derivation path runs. That makes the migration breaking instead of merely “not required”.
Minimal compatibility shim
type Software struct {
+ // Deprecated: categories are now derived from referenced software.
+ // Keep this field temporarily so older GitOps repos still parse.
+ SelfServiceCategories optjson.Slice[string] `json:"self_service_categories"`
Packages []SoftwarePackage `json:"packages"`
AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"`
FleetMaintainedApps []fleet.MaintainedAppSpec `json:"fleet_maintained_apps"`
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| type Software struct { | |
| Packages []SoftwarePackage `json:"packages"` | |
| AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"` | |
| FleetMaintainedApps []fleet.MaintainedAppSpec `json:"fleet_maintained_apps"` | |
| SelfServiceCategories optjson.Slice[string] `json:"self_service_categories"` | |
| Packages []SoftwarePackage `json:"packages"` | |
| AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"` | |
| FleetMaintainedApps []fleet.MaintainedAppSpec `json:"fleet_maintained_apps"` | |
| } | |
| type Software struct { | |
| // Deprecated: categories are now derived from referenced software. | |
| // Keep this field temporarily so older GitOps repos still parse. | |
| SelfServiceCategories optjson.Slice[string] `json:"self_service_categories"` | |
| Packages []SoftwarePackage `json:"packages"` | |
| AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"` | |
| FleetMaintainedApps []fleet.MaintainedAppSpec `json:"fleet_maintained_apps"` | |
| } |
🤖 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 312 - 316, Add a parse-only deprecated alias
field to the existing Software struct so existing GitOps YAML with
software.self_service_categories doesn't hard-fail: add a new field
SelfServiceCategories of type json.RawMessage (or interface{}) to type Software
with tags json:"self_service_categories,omitempty" and
yaml:"self_service_categories,omitempty" and a comment like "// Deprecated:
parse-only alias for backward compatibility" and ensure parseSoftware continues
to validate against Software but any code paths that produce/derive categories
ignore this field (do not use it in upstream logic).
There was a problem hiding this comment.
Not needed, the self_service_categories key is never going to be shipped
Related issue: Resolves #
Checklist for submitter
If some of the following don't apply, delete the relevant line.
Changes file added for user-visible changes in
changes/,orbit/changes/oree/fleetd-chrome/changes.See Changes files for more information.
Input data is properly validated,
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.Timeouts are implemented and retries are limited to avoid infinite loops
If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes
Testing
Added/updated automated tests
Where appropriate, automated tests simulate multiple hosts and test for host isolation (updates to one hosts's records do not affect another)
QA'd all new/changed functionality manually
Summary by CodeRabbit
New Features
Bug Fixes
Chores