Skip to content

Allow conditional downloads across fleets - #43679

Merged
sgress454 merged 1 commit into
mainfrom
sgress454/43417-cond-download-on-mult-fleet
Apr 21, 2026
Merged

Allow conditional downloads across fleets#43679
sgress454 merged 1 commit into
mainfrom
sgress454/43417-cond-download-on-mult-fleet

Conversation

@sgress454

@sgress454 sgress454 commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Related issue: Resolves #43417

Checklist for submitter

If some of the following don't apply, delete the relevant line.

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually
    • Using a local fileserver, added the same software to two fleets and ran fleetctl gitops. Verified that the first fleet downloaded the file, the second fleet used the cache, and both fleet showed the software installer in the UI.

Summary by CodeRabbit

  • Chores
    • Updated software installer lookup mechanism to support optional team-scoped searches, enabling fallback to cross-team installer cache when team-specific installers are unavailable.

@codecov

codecov Bot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.25000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.94%. Comparing base (378fc32) to head (dd651d9).
⚠️ Report is 98 commits behind head on main.

Files with missing lines Patch % Lines
ee/server/service/software_installers.go 50.00% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #43679      +/-   ##
==========================================
+ Coverage   66.91%   66.94%   +0.02%     
==========================================
  Files        2600     2600              
  Lines      208710   208718       +8     
  Branches     9339     9339              
==========================================
+ Hits       139666   139716      +50     
+ Misses      56327    56297      -30     
+ Partials    12717    12705      -12     
Flag Coverage Δ
backend 68.73% <81.25%> (+0.02%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

@sgress454
sgress454 force-pushed the sgress454/43417-cond-download-on-mult-fleet branch from 0e31e56 to dd651d9 Compare April 17, 2026 13:43
@sgress454

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

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.

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The GetInstallerByTeamAndURL method signature is updated across the datastore interface, implementation, and mocks to accept an optional team identifier (teamID *uint) instead of a required one. This enables conditional-download URL cache lookup to perform a two-step fallback: first searching within the current team, then across all teams if no match is found. The SQL query now conditionally filters by team when a team ID is provided, allowing installers to be reused across multiple teams when they share the same software package URL.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description includes required checklist items but lacks clarity on database/migration changes and detailed testing methodology. Clarify whether database schema changes are needed and provide more detail on manual QA testing methodology (e.g., specific scenarios, team configurations tested).
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: enabling conditional downloads to work across multiple fleets/teams, which matches the primary objective of avoiding re-downloads when multiple fleets declare the same package.
Linked Issues check ✅ Passed The code changes directly implement the linked issue's requirement by modifying GetInstallerByTeamAndURL to accept optional team scoping (nil for cross-team lookup), enabling software packages to be downloaded once and reused across fleets via conditional downloads with fallback logic.
Out of Scope Changes check ✅ Passed All changes are directly scoped to enabling cross-team conditional downloads: datastore interface updates, implementation changes, test updates, and fallback logic in software batch upload. No unrelated refactoring or feature additions detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 sgress454/43417-cond-download-on-mult-fleet

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 and usage tips.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ee/server/service/software_installers.go (1)

2690-2712: ⚠️ Potential issue | 🟡 Minor

Cross-team 304 cache-hit bypasses the authz check used by the sibling cross-team copy path.

When the cross-team lookup succeeds and the server returns 304, fillSoftwareInstallerPayloadFromExisting copies metadata (filename, version, title, bundle_identifier, etc.) from an installer owned by a potentially different team, with no check that the current viewer has write access to that team. Contrast this with the pre-existing cross-team copy path at Lines 2559–2595, which explicitly walks each team and skips ones the user isn't authorized for (svc.authz.Authorize(userctx, &fleet.SoftwareInstaller{TeamID: tmIDPtr}, fleet.ActionWrite)).

In practice this is narrow — the bytes are the same (keyed by SHA-256 / StorageID), and a user reaching this point has already authorized write on the current team — but the metadata being copied (e.g., Title, BundleIdentifier, Filename) can leak details of an installer on a team the user can't see. Consider either (a) restricting the cross-team fallback lookup at Line 2663 to installers the user can access, or (b) only adopting existingForCache for same-team matches and using cross-team solely to obtain an ETag.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ee/server/service/software_installers.go` around lines 2690 - 2712, When
handling the 304 + existingForCache cache-hit path, enforce the same
authorization check used in the cross-team copy path: call
svc.authz.Authorize(userctx, &fleet.SoftwareInstaller{TeamID:
existingForCache.TeamID}, fleet.ActionWrite) before invoking
fillSoftwareInstallerPayloadFromExisting or copying metadata from
existingForCache; if the authorization fails, do not copy metadata (either fall
back to re-downloading via retryDownload or only adopt the ETag/StorageID for
caching but leave Title/Filename/BundleIdentifier untouched). Update the branch
that currently sets p.MaintainedApp.SHA256, installer.HTTPETag, and cacheHit to
occur only after the authz check (or constrain to same-team matches) so metadata
from other teams is never leaked.
🧹 Nitpick comments (1)
server/datastore/mysql/software_installers_test.go (1)

4666-4676: Consider adding coverage for the multi-fleet cross-team fallback.

The nil-team assertion here is correct but doesn't fully exercise the PR's stated objective (reusing an already-downloaded package when multiple fleets declare the same URL). Only team1 has an installer at https://example.com/app/latest, so the ORDER BY si.id DESC LIMIT 1 path is trivially deterministic.

Consider adding a case where two teams both have an installer with the same URL (but different StorageIDs) and verifying that a nil-team lookup returns the most-recent active one. That would also guard against regressions in the ORDER BY id DESC + is_active = 1 interplay when teams share a URL — which is the core scenario this PR is intended to support.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/datastore/mysql/software_installers_test.go` around lines 4666 - 4676,
Add a test that exercises the multi-fleet cross-team fallback for
GetInstallerByTeamAndURL: create two teams (e.g., team1 and team2), insert
active installers for both with identical URL "https://example.com/app/latest"
but different StorageID values and different ids/timestamps, then call
ds.GetInstallerByTeamAndURL(ctx, nil, "https://example.com/app/latest") and
assert it returns the installer with the most-recent id (the expected active
StorageID); ensure one installer can be inactive (is_active = 0) to validate the
is_active filtering and the ORDER BY si.id DESC behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@ee/server/service/software_installers.go`:
- Around line 2690-2712: When handling the 304 + existingForCache cache-hit
path, enforce the same authorization check used in the cross-team copy path:
call svc.authz.Authorize(userctx, &fleet.SoftwareInstaller{TeamID:
existingForCache.TeamID}, fleet.ActionWrite) before invoking
fillSoftwareInstallerPayloadFromExisting or copying metadata from
existingForCache; if the authorization fails, do not copy metadata (either fall
back to re-downloading via retryDownload or only adopt the ETag/StorageID for
caching but leave Title/Filename/BundleIdentifier untouched). Update the branch
that currently sets p.MaintainedApp.SHA256, installer.HTTPETag, and cacheHit to
occur only after the authz check (or constrain to same-team matches) so metadata
from other teams is never leaked.

---

Nitpick comments:
In `@server/datastore/mysql/software_installers_test.go`:
- Around line 4666-4676: Add a test that exercises the multi-fleet cross-team
fallback for GetInstallerByTeamAndURL: create two teams (e.g., team1 and team2),
insert active installers for both with identical URL
"https://example.com/app/latest" but different StorageID values and different
ids/timestamps, then call ds.GetInstallerByTeamAndURL(ctx, nil,
"https://example.com/app/latest") and assert it returns the installer with the
most-recent id (the expected active StorageID); ensure one installer can be
inactive (is_active = 0) to validate the is_active filtering and the ORDER BY
si.id DESC behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 68205ec6-5872-4769-9af7-12a07f571b72

📥 Commits

Reviewing files that changed from the base of the PR and between 378fc32 and dd651d9.

📒 Files selected for processing (9)
  • cmd/fleetctl/fleetctl/gitops_test.go
  • cmd/fleetctl/fleetctl/testing_utils.go
  • cmd/fleetctl/integrationtest/gitops/software_test.go
  • ee/server/service/software_installers.go
  • server/datastore/mysql/software_installers.go
  • server/datastore/mysql/software_installers_test.go
  • server/fleet/datastore.go
  • server/mock/datastore_mock.go
  • server/service/software_installers_test.go

@sgress454
sgress454 marked this pull request as ready for review April 20, 2026 16:29
@sgress454
sgress454 requested a review from a team as a code owner April 20, 2026 16:29
Copilot AI review requested due to automatic review settings April 20, 2026 16:29

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

Copilot AI 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.

Pull request overview

Enables cross-team reuse of cached URL-based software installers during GitOps runs by allowing installer lookups to optionally fall back to “any team”, so the same URL isn’t re-downloaded separately per fleet/team.

Changes:

  • Updated GetInstallerByTeamAndURL to accept an optional teamID (*uint) where nil means “search across teams”.
  • Implemented cross-team fallback lookup in the conditional download path for software batch uploads.
  • Updated MySQL datastore implementation and expanded tests/mocks to cover the new signature and nil-team behavior.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
server/service/software_installers_test.go Updates datastore mock signature used by service tests.
server/mock/datastore_mock.go Updates mock datastore interface + method signature to teamID *uint.
server/fleet/datastore.go Updates datastore interface contract and documentation for cross-team fallback behavior.
server/datastore/mysql/software_installers_test.go Adjusts calls to pass *uint and adds coverage for teamID=nil fallback.
server/datastore/mysql/software_installers.go Implements optional team filtering; supports teamID=nil to search all teams.
ee/server/service/software_installers.go Adds same-team lookup first, then cross-team fallback for conditional downloads.
cmd/fleetctl/integrationtest/gitops/software_test.go Updates GitOps integration test mocks for new signature.
cmd/fleetctl/fleetctl/testing_utils.go Updates GitOps test mock setup for new signature.
cmd/fleetctl/fleetctl/gitops_test.go Updates fleetctl GitOps test mocks for new signature.
Comments suppressed due to low confidence (1)

ee/server/service/software_installers.go:2674

  • The cross-team fallback (GetInstallerByTeamAndURL(ctx, nil, ...)) can reuse an installer from another team without checking whether the requesting user has access to that source team. Unlike the earlier cross-team copy-by-hash logic in this same function (which authorizes per team), this could let a team-scoped user leverage cached bytes/metadata from a team they’re not permitted to access. Consider authorizing against existing.TeamID (including the nil/global case) before using its ETag/storage_id; if unauthorized, ignore the fallback and proceed with a normal download.
						// First try same-team lookup, then fall back to any team.
						existing, lookupErr := svc.ds.GetInstallerByTeamAndURL(ctx, &tmID, p.URL)
						if lookupErr != nil {
							svc.logger.WarnContext(ctx, "conditional download lookup failed, will download normally", "url", p.URL, "err", lookupErr)
						} else if existing == nil {
							// Cross-team fallback: another team may already have this URL cached.
							existing, lookupErr = svc.ds.GetInstallerByTeamAndURL(ctx, nil, p.URL)
							if lookupErr != nil {
								svc.logger.WarnContext(ctx, "cross-team conditional download lookup failed, will download normally", "url", p.URL, "err", lookupErr)
							}
						}
						if lookupErr == nil && existing != nil && existing.StorageID != "" &&
							existing.HTTPETag != nil && *existing.HTTPETag != "" &&
							existing.Extension != "ipa" && // skip conditional download for .ipa (multi-platform extraInstallers)
							validETag(*existing.HTTPETag) { // re-validate before use as defense-in-depth
							existingForCache = existing
							ifNoneMatch = *existing.HTTPETag
						}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/datastore/mysql/software_installers.go
Comment thread server/datastore/mysql/software_installers.go

@getvictor getvictor 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

@sgress454
sgress454 merged commit 91d9b25 into main Apr 21, 2026
57 of 59 checks passed
@sgress454
sgress454 deleted the sgress454/43417-cond-download-on-mult-fleet branch April 21, 2026 22:06
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.

Don't redownload custom packages that are shared across multiple fleets

4 participants