Fix batch set installers not updating some fields (#40631)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #40549 
Fix some settings like setup experience, self service, scripts, not
being updated in BatchSetSoftwareInstallers

## Testing

- [x] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [x] QA'd all new/changed functionality manually
- Tested that unlocked version FMA setup experience, self service, or
script changes appropriately
- Tested with version locked FMA

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

- [ ] Confirmed that the fix is not expected to adversely impact load
test results
- [ ] Alerted the release DRI if additional load testing is needed
This commit is contained in:
Jonathan Katz
2026-02-26 15:27:16 -05:00
committed by GitHub
parent b8ad24b77b
commit 7d567d73bb
2 changed files with 123 additions and 2 deletions
+29 -2
View File
@@ -2294,6 +2294,19 @@ ON DUPLICATE KEY UPDATE
is_active = VALUES(is_active)
`
const updateInstaller = `
UPDATE
software_installers
SET
install_during_setup = COALESCE(?, install_during_setup),
self_service = ?,
install_script_content_id = ?,
uninstall_script_content_id = ?,
post_install_script_content_id = ?,
pre_install_query = ?
WHERE id = ?
`
const loadSoftwareInstallerID = `
SELECT
id
@@ -2715,8 +2728,8 @@ WHERE
// for this team+title. This prevents duplicate rows from repeated batch sets
// that re-download the same latest version.
var skipInsert bool
var existingID uint
if installer.FleetMaintainedAppID != nil {
var existingID uint
err := sqlx.GetContext(ctx, tx, &existingID, `
SELECT id FROM software_installers
WHERE global_or_team_id = ? AND title_id = ? AND fleet_maintained_app_id IS NOT NULL AND version = ?
@@ -2729,7 +2742,21 @@ WHERE
}
}
if !skipInsert {
if skipInsert {
// some fields still need to be updated
args := []any{
installer.InstallDuringSetup,
installer.SelfService,
installScriptID,
uninstallScriptID,
postInstallScriptID,
installer.PreInstallQuery,
existingID,
}
if _, err := tx.ExecContext(ctx, updateInstaller, args...); err != nil {
return ctxerr.Wrapf(ctx, err, "updating existing installer with name %q", installer.Filename)
}
} else {
upsertQuery := insertNewOrEditedInstaller
if len(existing) > 0 && existing[0].IsPackageModified { // update uploaded_at for updated installer package
upsertQuery = fmt.Sprintf("%s, uploaded_at = NOW()", upsertQuery)
@@ -57,6 +57,7 @@ func TestSoftwareInstallers(t *testing.T) {
{"SoftwareInstallerReplicaLag", testSoftwareInstallerReplicaLag},
{"SoftwareTitleDisplayName", testSoftwareTitleDisplayName},
{"AddSoftwareTitleToMatchingSoftware", testAddSoftwareTitleToMatchingSoftware},
{"FleetMaintainedAppInstallerUpdates", testFleetMaintainedAppInstallerUpdates},
}
for _, c := range cases {
@@ -4275,3 +4276,96 @@ func testAddSoftwareTitleToMatchingSoftware(t *testing.T, ds *Datastore) {
})
}
func testFleetMaintainedAppInstallerUpdates(t *testing.T, ds *Datastore) {
ctx := t.Context()
user := test.NewUser(t, ds, "Alice", "alice@example.com", true)
tfr, err := fleet.NewTempFileReader(strings.NewReader("file contents"), t.TempDir)
require.NoError(t, err)
maintainedApp, err := ds.UpsertMaintainedApp(ctx, &fleet.MaintainedApp{
Name: "Maintained1",
Slug: "maintained1",
Platform: "darwin",
UniqueIdentifier: "fleet.maintained1",
})
require.NoError(t, err)
installerID, _, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
Title: "testpkg",
Source: "apps",
Platform: "darwin",
PreInstallQuery: "SELECT 1",
InstallScript: "echo install",
PostInstallScript: "echo post install",
UninstallScript: "echo uninstall",
InstallerFile: tfr,
StorageID: "storageid1",
Filename: "test.pkg",
Version: "1.0",
UserID: user.ID,
ValidatedLabels: &fleet.LabelIdentsWithScope{},
FleetMaintainedAppID: ptr.Uint(maintainedApp.ID),
InstallDuringSetup: ptr.Bool(false),
SelfService: false,
})
require.NoError(t, err)
tmFilter := fleet.TeamFilter{User: test.UserAdmin}
titles, _, _, err := ds.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{TeamID: ptr.Uint(0), Platform: "darwin", AvailableForInstall: true}, tmFilter)
require.NoError(t, err)
require.Len(t, titles, 1)
require.False(t, *titles[0].SoftwarePackage.InstallDuringSetup)
require.False(t, *titles[0].SoftwarePackage.SelfService)
installer, err := ds.GetSoftwareInstallerMetadataByID(ctx, installerID)
require.NoError(t, err)
require.NotNil(t, installer)
installScript := installer.InstallScriptContentID
postInstallScript := installer.PostInstallScriptContentID
uninstallScript := installer.UninstallScriptContentID
require.NotZero(t, installScript)
require.NotZero(t, postInstallScript)
require.NotZero(t, uninstallScript)
require.Equal(t, "SELECT 1", installer.PreInstallQuery)
// batch add the installer with different scripts, setup experience, self service
err = ds.BatchSetSoftwareInstallers(ctx, nil, []*fleet.UploadSoftwareInstallerPayload{
{
Title: "testpkg",
Source: "apps",
PreInstallQuery: "SELECT 1 DIFFERENT",
InstallScript: "echo install 2",
PostInstallScript: "echo post install 2",
UninstallScript: "echo uninstall 2",
InstallerFile: tfr,
StorageID: "storageid1",
Filename: "test.pkg",
Version: "1.0",
UserID: user.ID,
ValidatedLabels: &fleet.LabelIdentsWithScope{},
FleetMaintainedAppID: ptr.Uint(maintainedApp.ID),
InstallDuringSetup: ptr.Bool(true),
SelfService: true,
},
})
require.NoError(t, err)
titles, _, _, err = ds.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{TeamID: ptr.Uint(0), Platform: "darwin", AvailableForInstall: true}, tmFilter)
require.NoError(t, err)
require.Len(t, titles, 1)
require.True(t, *titles[0].SoftwarePackage.InstallDuringSetup)
require.True(t, *titles[0].SoftwarePackage.SelfService)
installer, err = ds.GetSoftwareInstallerMetadataByID(ctx, installerID)
require.NoError(t, err)
require.NotNil(t, installer)
// all fields that should have changed did change
require.NotEqual(t, installScript, installer.InstallScriptContentID)
require.NotEqual(t, postInstallScript, installer.PostInstallScriptContentID)
require.NotEqual(t, uninstallScript, installer.UninstallScriptContentID)
require.Equal(t, "SELECT 1 DIFFERENT", installer.PreInstallQuery)
}