Skip to content

Fix FMA pinning not changing patch policy - #49519

Merged
jkatz01 merged 3 commits into
mainfrom
49474-fma-pin-not-changing-patch-policy
Jul 17, 2026
Merged

Fix FMA pinning not changing patch policy#49519
jkatz01 merged 3 commits into
mainfrom
49474-fma-pin-not-changing-patch-policy

Conversation

@jkatz01

@jkatz01 jkatz01 commented Jul 17, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #49474

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/ or ee/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

Summary by CodeRabbit

  • Bug Fixes
    • Fixed patch policies for Fleet-maintained apps not updating when the active app version changes.
    • Patch policy queries now refresh to match the currently active (or pinned) installer version, including changes driven by pinning, cron, and GitOps.
    • Improved behavior when pins are cleared or switched, ensuring the policy continues referencing the correct version-specific query.
  • Tests
    • Expanded integration coverage to verify version-pinned patch policy queries across scenario updates.

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 47.05882% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.82%. Comparing base (859e2e0) to head (544613d).
⚠️ Report is 16 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/software_installers.go 47.05% 6 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49519      +/-   ##
==========================================
- Coverage   68.08%   67.82%   -0.27%     
==========================================
  Files        3882     3888       +6     
  Lines      246398   247553    +1155     
  Branches    13169    13169              
==========================================
+ Hits       167753   167891     +138     
- Misses      63504    64505    +1001     
- Partials    15141    15157      +16     
Flag Coverage Δ
backend 69.22% <47.05%> (-0.32%) ⬇️

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.

@jkatz01
jkatz01 marked this pull request as ready for review July 17, 2026 20:52
@jkatz01
jkatz01 requested a review from a team as a code owner July 17, 2026 20:52
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ad7c46bd-7b1b-448b-8639-13d7f7844613

📥 Commits

Reviewing files that changed from the base of the PR and between b088351 and 544613d.

📒 Files selected for processing (3)
  • server/datastore/mysql/software_installers.go
  • server/datastore/mysql/software_installers_test.go
  • server/service/integration_enterprise_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/datastore/mysql/software_installers_test.go
  • server/service/integration_enterprise_test.go

Walkthrough

Fleet-maintained app activation now updates matching patch policy queries from the active installer’s version-specific patch_query. Datastore tests cover installer flips and pin changes, while enterprise integration tests verify query updates for Cloudflare WARP and Zoom during cron, GitOps, and version-pinning flows.

Possibly related PRs

  • fleetdm/fleet#49525: Modifies the same installer activation path to redirect queued installs and retries to the newly active installer.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: FMA pinning now updates the patch policy.
Description check ✅ Passed The description includes the related issue, checklist, testing notes, and manual QA context.
Linked Issues check ✅ Passed The code and tests address #49474 by keeping patch policy queries aligned with the active or pinned installer version.
Out of Scope Changes check ✅ Passed The changes stay focused on the patch policy update bug and related test coverage, with no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 49474-fma-pin-not-changing-patch-policy

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.1)
server/service/integration_enterprise_test.go

ast-grep timed out on this file


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: 2

🤖 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/datastore/mysql/software_installers.go`:
- Around line 705-714: Update the patch policy UPDATE in the active installer
flow to use MySQL’s null-safe equality operator (<=>) for p.team_id and pass
payload.TeamID directly instead of tmID, so both global NULL team IDs and scoped
team IDs match precisely. Keep the existing title and installer filters
unchanged.

In `@server/service/integration_enterprise_test.go`:
- Around line 28180-28191: Update the POST route in the patch policy setup to
use the team-scoped “teams” segment instead of “fleets”, matching the GET route
used by patchPolicyQuery and preserving the existing team.ID and policies
endpoint.
🪄 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: e12427b1-b2df-4e20-9194-d7cdcbc6a8e1

📥 Commits

Reviewing files that changed from the base of the PR and between 4d07660 and b088351.

📒 Files selected for processing (4)
  • changes/49474-fix-patch-policy-query-not-updating
  • server/datastore/mysql/software_installers.go
  • server/datastore/mysql/software_installers_test.go
  • server/service/integration_enterprise_test.go

Comment on lines +705 to +714
// Edit the patch policy if it exists to use the pinned installer's query
if _, err := tx.ExecContext(ctx, `
UPDATE policies p
JOIN software_installers si ON si.id = ?
SET p.query = si.patch_query
WHERE p.team_id = ? AND p.patch_software_title_id = ?
`, activeInstallerID, tmID, payload.TitleID); err != nil {
return ctxerr.Wrap(ctx, err, "updating patch policy query for active fleet-maintained app installer")
}

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.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Fix global policy matching to gracefully handle NULL.

For global policies, payload.TeamID is nil and tmID resolves to 0. However, global policies have team_id = NULL in the database. The WHERE p.team_id = ? clause with 0 will evaluate to NULL = 0 (which is false in MySQL), causing the update to silently fail and leave the global patch policy query unchanged.

As per path instructions, ensure that appropriate filtering criteria are applied to prevent non-deterministic or incorrect results due to a lack of precise scoping. Update the condition to use the null-safe equality operator <=> and pass the pointer directly. The database driver will correctly translate a nil Go pointer to a SQL NULL.

🐛 Proposed fix
 		// Edit the patch policy if it exists to use the pinned installer's query
 		if _, err := tx.ExecContext(ctx, `
 			UPDATE policies p
 			JOIN software_installers si ON si.id = ?
 			SET p.query = si.patch_query
-			WHERE p.team_id = ? AND p.patch_software_title_id = ?
-		`, activeInstallerID, tmID, payload.TitleID); err != nil {
+			WHERE p.team_id <=> ? AND p.patch_software_title_id = ?
+		`, activeInstallerID, payload.TeamID, payload.TitleID); err != nil {
 			return ctxerr.Wrap(ctx, err, "updating patch policy query for active fleet-maintained app installer")
 		}
📝 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.

Suggested change
// Edit the patch policy if it exists to use the pinned installer's query
if _, err := tx.ExecContext(ctx, `
UPDATE policies p
JOIN software_installers si ON si.id = ?
SET p.query = si.patch_query
WHERE p.team_id = ? AND p.patch_software_title_id = ?
`, activeInstallerID, tmID, payload.TitleID); err != nil {
return ctxerr.Wrap(ctx, err, "updating patch policy query for active fleet-maintained app installer")
}
// Edit the patch policy if it exists to use the pinned installer's query
if _, err := tx.ExecContext(ctx, `
UPDATE policies p
JOIN software_installers si ON si.id = ?
SET p.query = si.patch_query
WHERE p.team_id <=> ? AND p.patch_software_title_id = ?
`, activeInstallerID, payload.TeamID, payload.TitleID); err != nil {
return ctxerr.Wrap(ctx, err, "updating patch policy query for active fleet-maintained app installer")
}
🤖 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/datastore/mysql/software_installers.go` around lines 705 - 714, Update
the patch policy UPDATE in the active installer flow to use MySQL’s null-safe
equality operator (<=>) for p.team_id and pass payload.TeamID directly instead
of tmID, so both global NULL team IDs and scoped team IDs match precisely. Keep
the existing title and installer filters unchanged.

Source: Path instructions

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.

Patch policies cannot be created globally

Comment thread server/service/integration_enterprise_test.go
Comment thread server/datastore/mysql/software_installers.go Outdated
Comment thread server/datastore/mysql/software_installers.go Outdated
Comment thread server/datastore/mysql/software_installers.go Outdated
cdcme
cdcme previously approved these changes Jul 17, 2026

@cdcme cdcme 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.

Few questions, but looks clean!

@jkatz01
jkatz01 marked this pull request as draft July 17, 2026 21:34
@jkatz01
jkatz01 marked this pull request as ready for review July 17, 2026 22:56
@jkatz01
jkatz01 merged commit 8a33fcd into main Jul 17, 2026
45 checks passed
@jkatz01
jkatz01 deleted the 49474-fma-pin-not-changing-patch-policy branch July 17, 2026 23:00
georgekarrv pushed a commit that referenced this pull request Jul 22, 2026
…n RC branch

The FMA patch-policy test cherry-picked in with #49519 calls
RecordPolicyQueryExecutions expecting ([]uint, error) (its signature on main),
but on this RC branch it returns just error. The non-compiling test package
failed both the mysql test suite and the Go linters' typecheck pass. Drop the
unused first return value to match the RC signature.
georgekarrv added a commit that referenced this pull request Jul 22, 2026
…TeamTitleAndInstallerID (#49760)

**Related issue:** N/A — RC branch build/CI fix

## What & why

The Docker publish for **4.89.2** on `rc-patch-fleet-v4.89.2` was
failing to compile, and the `mysql` test suite + Go linters were red —
all from the same root cause: a **broken cherry-pick**.

**1. Missing datastore method (broke Docker publish + all binary
builds):**

```
server/datastore/mysql/software_installers.go:748:30:
  ds.GetSoftwareInstallerMetadataByTeamTitleAndInstallerID undefined
```

The FMA patch-policy fix (#49519, commit `9603e84cc4`) was cherry-picked
in, bringing a *caller* of
`GetSoftwareInstallerMetadataByTeamTitleAndInstallerID`, but the method
itself was introduced on `main` by the large "Multiple packages API
changes" feature PR (#48607), which is not part of this release.

**2. Stale test assertion (broke `mysql` test suite +
`lint`/`lint-incremental` typecheck):**

```
server/datastore/mysql/software_installers_test.go:6362:11:
  assignment mismatch: 2 variables but ds.RecordPolicyQueryExecutions returns 1 value
```

The same cherry-pick brought a test that calls
`RecordPolicyQueryExecutions` expecting `([]uint, error)` (its signature
on `main`), but on this RC branch it returns just `error`. A
non-compiling test package fails both the mysql suite and the linter's
typecheck pass.

## Fix

- Added `GetSoftwareInstallerMetadataByTeamTitleAndInstallerID` as a
self-contained, minimal addition (interface + datastore impl +
regenerated mock), refactoring
`GetSoftwareInstallerMetadataByTeamAndTitleID` to share a private helper
that takes an optional installer ID. The `nil` path preserves existing
behavior exactly; a non-nil ID selects that specific package by `si.id`.
- Fixed the stale test call to match the RC branch's single-return
signature (the discarded first return value is simply dropped).

No production behavior change for existing callers.

# Checklist for submitter

- [x] 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.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

For unreleased bug fixes in a release candidate, one of:

- [x] Confirmed that the fix is not expected to adversely impact load
test results

---------

Co-authored-by: test <test@test.com>
@coderabbitai coderabbitai Bot mentioned this pull request Aug 7, 2026
15 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.

FMA version pinning doesn't change patch policy

2 participants