VPP: fix matching software title to existing one if one exists (#20534)

This commit is contained in:
Martin Angers 2024-07-17 15:41:51 -04:00 committed by GitHub
parent 74b1dc4f0d
commit e115516e8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 12 deletions

View file

@ -141,9 +141,7 @@ func (ds *Datastore) BatchInsertVPPApps(ctx context.Context, apps []*fleet.VPPAp
func (ds *Datastore) InsertVPPAppWithTeam(ctx context.Context, app *fleet.VPPApp, teamID *uint) error {
return ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
// TODO: we should not just create a software title, I think we should try
// to match to an existing one (i.e. getOrCreate logic)?
titleID, err := insertSoftwareTitleForVPPApp(ctx, tx, app)
titleID, err := ds.getOrInsertSoftwareTitleForVPPApp(ctx, tx, app)
if err != nil {
return err
}
@ -234,17 +232,40 @@ VALUES
return ctxerr.Wrap(ctx, err, "writing vpp app team mapping to db")
}
func insertSoftwareTitleForVPPApp(ctx context.Context, tx sqlx.ExtContext, app *fleet.VPPApp) (uint, error) {
stmt := `INSERT INTO software_titles (name, source, bundle_identifier, browser) VALUES (?, '', ?, '')`
func (ds *Datastore) getOrInsertSoftwareTitleForVPPApp(ctx context.Context, tx sqlx.ExtContext, app *fleet.VPPApp) (uint, error) {
// NOTE: it was decided to leave the source empty for VPP apps for now, TBD
// if this needs to change to better map to how software titles are reported
// back by osquery. Since I think this will likely not stay empty, I'm using
// a variable for the source.
const source = ""
result, err := tx.ExecContext(ctx, stmt, app.Name, app.BundleIdentifier)
if err != nil {
return 0, ctxerr.Wrap(ctx, err, "writing vpp app software title")
selectStmt := `SELECT id FROM software_titles WHERE name = ? AND source = ? AND browser = ''`
selectArgs := []any{app.Name, source}
insertStmt := `INSERT INTO software_titles (name, source, browser) VALUES (?, ?, '')`
insertArgs := []any{app.Name, source}
if app.BundleIdentifier != "" {
selectStmt = `SELECT id FROM software_titles WHERE bundle_identifier = ?`
selectArgs = []any{app.BundleIdentifier}
insertStmt = `INSERT INTO software_titles (name, source, bundle_identifier, browser) VALUES (?, ?, ?, '')`
insertArgs = append(insertArgs, app.BundleIdentifier)
}
id, _ := result.LastInsertId()
titleID, err := ds.optimisticGetOrInsert(ctx,
&parameterizedStmt{
Statement: selectStmt,
Args: selectArgs,
},
&parameterizedStmt{
Statement: insertStmt,
Args: insertArgs,
},
)
if err != nil {
return 0, err
}
return uint(id), nil
return titleID, nil
}
func (ds *Datastore) DeleteVPPAppFromTeam(ctx context.Context, teamID *uint, adamID string) error {

View file

@ -300,7 +300,27 @@ func testVPPApps(t *testing.T, ds *Datastore) {
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "foobar"})
require.NoError(t, err)
// Insert some VPP apps for the team
// create a host with some non-VPP software
h1, err := ds.NewHost(ctx, &fleet.Host{
Hostname: "macos-test-1",
OsqueryHostID: ptr.String("osquery-macos-1"),
NodeKey: ptr.String("node-key-macos-1"),
UUID: uuid.NewString(),
Platform: "darwin",
HardwareSerial: "654321a",
})
require.NoError(t, err)
software := []fleet.Software{
{Name: "foo", Version: "0.0.1", BundleIdentifier: "b1"},
{Name: "foo", Version: "0.0.2", BundleIdentifier: "b1"},
{Name: "bar", Version: "0.0.3", BundleIdentifier: "bar"},
}
_, err = ds.UpdateHostSoftware(ctx, h1.ID, software)
require.NoError(t, err)
err = ds.ReconcileSoftwareTitles(ctx)
require.NoError(t, err)
// Insert some VPP apps for the team, "vpp_app_1" should match the existing "foo" title
app1 := &fleet.VPPApp{Name: "vpp_app_1", AdamID: "1", BundleIdentifier: "b1"}
app2 := &fleet.VPPApp{Name: "vpp_app_2", AdamID: "2", BundleIdentifier: "b2"}
err = ds.InsertVPPAppWithTeam(ctx, app1, &team.ID)
@ -332,6 +352,6 @@ func testVPPApps(t *testing.T, ds *Datastore) {
require.Len(t, appTitles, 2)
require.Equal(t, app1.BundleIdentifier, *appTitles[0].BundleIdentifier)
require.Equal(t, app2.BundleIdentifier, *appTitles[1].BundleIdentifier)
require.Equal(t, app1.Name, appTitles[0].Name)
require.Equal(t, "foo", appTitles[0].Name)
require.Equal(t, app2.Name, appTitles[1].Name)
}