Skip to content

de-dup host_software and host_vpp_software_installs #28185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 14, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions server/datastore/mysql/software.go
Original file line number Diff line number Diff line change
@@ -2722,6 +2722,7 @@ func hostVPPInstalls(ds *Datastore, ctx context.Context, hostID uint, globalOrTe
vppInstallsStmt := fmt.Sprintf(`
( -- upcoming_vpp_install
SELECT
vpp_apps.title_id AS id,
ua.execution_id AS last_install_install_uuid,
ua.created_at AS last_install_installed_at,
vaua.adam_id AS vpp_app_adam_id,
@@ -2740,7 +2741,9 @@ func hostVPPInstalls(ds *Datastore, ctx context.Context, hostID uint, globalOrTe
(ua2.priority < ua.priority OR ua2.created_at > ua.created_at)
LEFT JOIN
vpp_apps_teams vat ON vaua.adam_id = vat.adam_id AND vaua.platform = vat.platform AND vat.global_or_team_id = :global_or_team_id
WHERE
INNER JOIN
vpp_apps ON vaua.adam_id = vpp_apps.adam_id AND vaua.platform = vpp_apps.platform
WHERE
-- selfServiceFilter
%s
ua.host_id = :host_id AND
@@ -2749,6 +2752,7 @@ func hostVPPInstalls(ds *Datastore, ctx context.Context, hostID uint, globalOrTe
) UNION (
-- last_vpp_install
SELECT
vpp_apps.title_id AS id,
hvsi.command_uuid AS last_install_install_uuid,
hvsi.created_at AS last_install_installed_at,
hvsi.adam_id AS vpp_app_adam_id,
@@ -2767,7 +2771,9 @@ func hostVPPInstalls(ds *Datastore, ctx context.Context, hostID uint, globalOrTe
(hvsi.created_at < hvsi2.created_at OR (hvsi.created_at = hvsi2.created_at AND hvsi.id < hvsi2.id))
LEFT JOIN
vpp_apps_teams vat ON hvsi.adam_id = vat.adam_id AND hvsi.platform = vat.platform AND vat.global_or_team_id = :global_or_team_id
WHERE
INNER JOIN
vpp_apps ON hvsi.adam_id = vpp_apps.adam_id AND hvsi.platform = vpp_apps.platform
WHERE
-- selfServiceFilter
%s
hvsi.host_id = :host_id AND
@@ -2966,6 +2972,7 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
}

hostInstalledSoftware, err := hostInstalledSoftware(ds, ctx, host.ID)
hostInstalledSoftwareTitleSet := make(map[uint]struct{})
if err != nil {
return nil, nil, err
}
@@ -2978,6 +2985,7 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt

if s.SoftwareID != nil {
bySoftwareID[*s.SoftwareID] = s
hostInstalledSoftwareTitleSet[*s.SoftwareID] = struct{}{}
}
}

@@ -2988,6 +2996,12 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
byVPPAdamID := make(map[string]*hostSoftware)
for _, s := range hostVPPInstalls {
if s.VPPAppAdamID != nil {
// If a VPP app is already installed on the host, we don't need to double count it
// until we merge the two fetch queries later on in this method
// we have to manually remove any VPP apps that are also being returned by the host_software table
if _, exists := hostInstalledSoftwareTitleSet[s.ID]; exists {
continue
}
byVPPAdamID[*s.VPPAppAdamID] = s
}
}
72 changes: 72 additions & 0 deletions server/datastore/mysql/software_test.go
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@ func TestSoftware(t *testing.T) {
{"VerifySoftwareChecksum", testVerifySoftwareChecksum},
{"ListHostSoftware", testListHostSoftware},
{"ListIOSHostSoftware", testListIOSHostSoftware},
{"ListHostSoftwareWithVPPApps", testListHostSoftwareWithVPPApps},
{"SetHostSoftwareInstallResult", testSetHostSoftwareInstallResult},
{"ListHostSoftwareInstallThenTransferTeam", testListHostSoftwareInstallThenTransferTeam},
{"ListHostSoftwareInstallThenDeleteInstallers", testListHostSoftwareInstallThenDeleteInstallers},
@@ -4779,6 +4780,77 @@ func testListIOSHostSoftware(t *testing.T, ds *Datastore) {
opts.OnlyAvailableForInstall = false
}

func testListHostSoftwareWithVPPApps(t *testing.T, ds *Datastore) {
ctx := context.Background()

tm, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1"})
require.NoError(t, err)
host := test.NewHost(t, ds, "host1", "", "host1key", "host1uuid", time.Now())
nanoEnroll(t, ds, host, false)
user := test.NewUser(t, ds, "Alice", "alice@example.com", true)

err = ds.AddHostsToTeam(ctx, &tm.ID, []uint{host.ID})
require.NoError(t, err)
host.TeamID = &tm.ID
numberOfApps := 5

software := []fleet.Software{}
for i := 0; i < numberOfApps; i++ {
software = append(software, fleet.Software{
Name: fmt.Sprintf("z%d", i),
Version: fmt.Sprintf("0.0.%d", i),
Source: "apps",
BundleIdentifier: fmt.Sprintf("com.example.%d", i),
})
}
_, err = ds.UpdateHostSoftware(ctx, host.ID, software)
require.NoError(t, err)

dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Test org"+t.Name(), "Test location"+t.Name())
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
require.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{})
require.NoError(t, err)
time.Sleep(time.Second)

vPPApp := &fleet.VPPApp{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the above, you should be able to repro that issue by adding the same VPP app 3x, once each per platform.

VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "adam_vpp_1", Platform: fleet.MacOSPlatform}},
Name: "vpp1",
BundleIdentifier: "com.app.vpp1",
}
va1, err := ds.InsertVPPAppWithTeam(ctx, vPPApp, &tm.ID)
require.NoError(t, err)
vpp1 := va1.AdamID
vpp1CmdUUID := createVPPAppInstallRequest(t, ds, host, vpp1, user)
_, err = ds.activateNextUpcomingActivity(ctx, ds.writer(ctx), host.ID, "")
require.NoError(t, err)
createVPPAppInstallResult(t, ds, host, vpp1CmdUUID, fleet.MDMAppleStatusAcknowledged)
// Insert software entry for vpp app
res, err := ds.writer(ctx).ExecContext(ctx, `
INSERT INTO software (name, version, source, bundle_identifier, title_id, checksum)
VALUES (?, ?, ?, ?, ?, ?)
`,
vPPApp.Name, vPPApp.LatestVersion, "apps", vPPApp.BundleIdentifier, vPPApp.TitleID, hex.EncodeToString([]byte("vpp1")),
)
require.NoError(t, err)
time.Sleep(time.Second)
softwareID, err := res.LastInsertId()
require.NoError(t, err)
_, err = ds.writer(ctx).ExecContext(ctx, `
INSERT INTO host_software (host_id, software_id)
VALUES (?, ?)
`, host.ID, softwareID)
require.NoError(t, err)

opts := fleet.HostSoftwareTitleListOptions{ListOptions: fleet.ListOptions{PerPage: uint(numberOfApps - 1), IncludeMetadata: true, OrderKey: "name", TestSecondaryOrderKey: "source"}}
sw, meta, err := ds.ListHostSoftware(ctx, host, opts)
require.NoError(t, err)
assert.Len(t, sw, numberOfApps-1)
assert.Equal(t, numberOfApps+1, int(meta.TotalResults))
assert.True(t, meta.HasNextResults)
}

func testSetHostSoftwareInstallResult(t *testing.T, ds *Datastore) {
ctx := context.Background()
host := test.NewHost(t, ds, "host1", "", "host1key", "host1uuid", time.Now())