Automatic install policies in ListHostSoftware (#31469)

Fixes #30197
# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
This commit is contained in:
Konstantin Sykulev
2025-08-01 10:22:14 -05:00
committed by GitHub
parent c3ee132108
commit 828504d038
3 changed files with 98 additions and 6 deletions
+1
View File
@@ -0,0 +1 @@
* Added automatic install policies into host software responses
+22 -1
View File
@@ -3792,8 +3792,10 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
}
var vppAdamIDs []string
for key := range byVPPAdamID {
var vppTitleIds []uint
for key, v := range byVPPAdamID {
vppAdamIDs = append(vppAdamIDs, key)
vppTitleIds = append(vppTitleIds, v.ID)
}
var titleCount uint
@@ -4126,6 +4128,17 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
vulnerabilitiesBySoftwareID[cve.SoftwareID] = append(vulnerabilitiesBySoftwareID[cve.SoftwareID], cve.CVE)
}
// Grab the automatic install policies, if any exist
policies, err := ds.getPoliciesBySoftwareTitleIDs(ctx, append(vppTitleIds, softwareTitleIds...), host.TeamID)
if err != nil {
return nil, nil, ctxerr.Wrap(ctx, err, "batch getting policies by software title IDs")
}
policiesBySoftwareTitleId := make(map[uint][]fleet.AutomaticInstallPolicy, len(policies))
for _, p := range policies {
policiesBySoftwareTitleId[p.TitleID] = append(policiesBySoftwareTitleId[p.TitleID], p)
}
indexOfSoftwareTitle := make(map[uint]uint)
deduplicatedList := make([]*hostSoftware, 0, len(hostSoftwareList))
for _, softwareTitleRecord := range hostSoftwareList {
@@ -4268,6 +4281,14 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
}
}
if policies, ok := policiesBySoftwareTitleId[softwareTitleRecord.ID]; ok {
if softwareTitleRecord.AppStoreApp != nil {
softwareTitleRecord.AppStoreApp.AutomaticInstallPolicies = policies
} else {
softwareTitleRecord.SoftwarePackage.AutomaticInstallPolicies = policies
}
}
if _, ok := indexOfSoftwareTitle[softwareTitleRecord.ID]; !ok {
indexOfSoftwareTitle[softwareTitleRecord.ID] = uint(len(deduplicatedList))
deduplicatedList = append(deduplicatedList, softwareTitleRecord)
+75 -5
View File
@@ -3702,6 +3702,13 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
assert.Equal(t, e.SoftwarePackage.LastUninstall.ExecutionID, g.SoftwarePackage.LastUninstall.ExecutionID)
assert.NotNil(t, g.SoftwarePackage.LastUninstall.UninstalledAt)
}
if e.SoftwarePackage.AutomaticInstallPolicies != nil {
require.Len(t, e.SoftwarePackage.AutomaticInstallPolicies, len(g.SoftwarePackage.AutomaticInstallPolicies))
for i, p := range e.SoftwarePackage.AutomaticInstallPolicies {
require.Equal(t, p.ID, g.SoftwarePackage.AutomaticInstallPolicies[i].ID)
require.Equal(t, p.Name, g.SoftwarePackage.AutomaticInstallPolicies[i].Name)
}
}
}
if e.AppStoreApp != nil {
@@ -3716,6 +3723,13 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
require.Equal(t, e.AppStoreApp.LastInstall.CommandUUID, g.AppStoreApp.LastInstall.CommandUUID)
require.NotNil(t, g.AppStoreApp.LastInstall.InstalledAt)
}
if e.AppStoreApp.AutomaticInstallPolicies != nil {
require.Len(t, e.AppStoreApp.AutomaticInstallPolicies, len(g.AppStoreApp.AutomaticInstallPolicies))
for i, p := range e.AppStoreApp.AutomaticInstallPolicies {
require.Equal(t, p.ID, g.AppStoreApp.AutomaticInstallPolicies[i].ID)
require.Equal(t, p.Name, g.AppStoreApp.AutomaticInstallPolicies[i].Name)
}
}
}
// require.Equal(t, e.SoftwarePackage, g.SoftwarePackage)
// require.Equal(t, e.AppStoreApp, g.AppStoreApp)
@@ -4220,6 +4234,33 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
i3.Name + i3.Source: expected[i3.Name+i3.Source],
}, sw, true)
// add policies to software
policy1 := newTestPolicy(t, ds, user, "policy 1", "darwin", &tm.ID)
policy1.SoftwareInstallerID = ptr.Uint(swi5Tm)
err = ds.SavePolicy(context.Background(), policy1, false, false)
require.NoError(t, err)
policy2 := newTestPolicy(t, ds, user, "policy 2", "darwin", &tm.ID)
policy2.SoftwareInstallerID = ptr.Uint(swi5Tm)
err = ds.SavePolicy(context.Background(), policy2, false, false)
require.NoError(t, err)
sw, meta, err = ds.ListHostSoftware(ctx, tmHost, opts)
require.NoError(t, err)
require.Equal(t, &fleet.PaginationMetadata{TotalResults: 1}, meta)
expectedWithPolicies := map[string]fleet.HostSoftwareWithInstaller{
i3.Name + i3.Source: expected[i3.Name+i3.Source],
}
expectedWithPolicies[i3.Name+i3.Source].SoftwarePackage.AutomaticInstallPolicies = []fleet.AutomaticInstallPolicy{
{
ID: policy1.ID,
Name: policy1.Name,
},
{
ID: policy2.ID,
Name: policy2.Name,
},
}
compareResults(expectedWithPolicies, sw, true)
// test with a search query (searches on name), with and without available software
opts.ListOptions.MatchQuery = "a"
opts.IncludeAvailableForInstall = false
@@ -4260,7 +4301,7 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
BundleIdentifier: "com.app.vpp1",
}, nil)
require.NoError(t, err)
_, err = ds.InsertVPPAppWithTeam(ctx,
vatm1, err := ds.InsertVPPAppWithTeam(ctx,
&fleet.VPPApp{
VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_1", Platform: fleet.MacOSPlatform}}, Name: "vpp1",
BundleIdentifier: "com.app.vpp1",
@@ -4303,6 +4344,20 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
vpp1TmCmdUUID := createVPPAppInstallRequest(t, ds, tmHost, vpp1, user)
require.NotEmpty(t, vpp1TmCmdUUID)
// add policies to vpp app 1
va1FromDB, err := ds.GetVPPAppMetadataByAdamIDPlatformTeamID(ctx, vatm1.AdamID, vatm1.Platform, &tm.ID)
require.NoError(t, err)
policy3 := newTestPolicy(t, ds, user, "policy 3", "darwin", &tm.ID)
policy3.VPPAppsTeamsID = &va1FromDB.AppTeamID
err = ds.SavePolicy(context.Background(), policy3, false, false)
require.NoError(t, err)
policy4 := newTestPolicy(t, ds, user, "policy 4", "darwin", &tm.ID)
policy4.VPPAppsTeamsID = &va1FromDB.AppTeamID
err = ds.SavePolicy(context.Background(), policy4, false, false)
require.NoError(t, err)
opts.IncludeAvailableForInstall = false
opts.ListOptions.MatchQuery = ""
sw, meta, err = ds.ListHostSoftware(ctx, host, opts)
@@ -4374,10 +4429,25 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
compareResults(map[string]fleet.HostSoftwareWithInstaller{
i3.Name + i3.Source: expected[i3.Name+i3.Source],
"vpp1apps": {
Name: "vpp1",
Source: "apps",
Status: expectStatus(fleet.SoftwareInstallPending),
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp1, Platform: "darwin", SelfService: ptr.Bool(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp1TmCmdUUID}},
Name: "vpp1",
Source: "apps",
Status: expectStatus(fleet.SoftwareInstallPending),
AppStoreApp: &fleet.SoftwarePackageOrApp{
AppStoreID: vpp1,
Platform: "darwin",
SelfService: ptr.Bool(false),
LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp1TmCmdUUID},
AutomaticInstallPolicies: []fleet.AutomaticInstallPolicy{
{
ID: policy3.ID,
Name: policy3.Name,
},
{
ID: policy4.ID,
Name: policy4.Name,
},
},
},
},
}, sw, true)