Support deletion host-referencing tables that use UUID instead of ID when deleting a host (#11017)

This commit is contained in:
Martin Angers 2023-04-05 16:29:28 -04:00 committed by GitHub
parent 242716c905
commit 231b8e4153
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 2 deletions

View File

@ -0,0 +1 @@
* Added missing tables to be cleared on host deletion (those that reference the host by UUID instead of ID).

View File

@ -2502,6 +2502,27 @@ func testGetMDMAppleCommandResults(t *testing.T, ds *Datastore) {
Result: []byte(rawCmd2),
},
})
// delete host [0] and verify that it did delete its command results
err = ds.DeleteHost(ctx, enrolledHosts[0].ID)
require.NoError(t, err)
// command now has only the hosts[1] result
res, err = ds.GetMDMAppleCommandResults(ctx, uuid2)
require.NoError(t, err)
require.Len(t, res, 1)
require.NotZero(t, res[0].UpdatedAt)
res[0].UpdatedAt = time.Time{}
require.ElementsMatch(t, res, []*fleet.MDMAppleCommandResult{
{
DeviceID: enrolledHosts[1].UUID,
CommandUUID: uuid2,
Status: "Error",
RequestType: "ProfileList",
Result: []byte(rawCmd2),
},
})
}
func createMDMAppleCommanderAndStorage(t *testing.T, ds *Datastore) (*apple_mdm.MDMAppleCommander, *NanoMDMStorage) {

View File

@ -333,6 +333,17 @@ var hostRefs = []string{
"host_disk_encryption_keys",
}
// those host refs cannot be deleted using the host.id like the hostRefs above,
// they use the host.uuid instead. Additionally, the column name that refers to
// the host.uuid is not always named the same, so the map key is the table name
// and the map value is the column name to match to the host.uuid.
var additionalHostRefsByUUID = map[string]string{
"host_mdm_apple_profiles": "host_uuid",
// deleting from nano_devices causes cascading deletes to nano_enrollments and
// any other tables that reference nano_devices.
"nano_devices": "id",
}
func (ds *Datastore) DeleteHost(ctx context.Context, hid uint) error {
delHostRef := func(tx sqlx.ExtContext, table string) error {
_, err := tx.ExecContext(ctx, fmt.Sprintf(`DELETE FROM %s WHERE host_id=?`, table), hid)
@ -342,6 +353,12 @@ func (ds *Datastore) DeleteHost(ctx context.Context, hid uint) error {
return nil
}
// load just the host uuid for the MDM tables that rely on this to be cleared.
var hostUUID string
if err := ds.writer.GetContext(ctx, &hostUUID, `SELECT uuid FROM hosts WHERE id = ?`, hid); err != nil {
return ctxerr.Wrapf(ctx, err, "get uuid for host %d", hid)
}
return ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
_, err := tx.ExecContext(ctx, `DELETE FROM hosts WHERE id = ?`, hid)
if err != nil {
@ -360,6 +377,15 @@ func (ds *Datastore) DeleteHost(ctx context.Context, hid uint) error {
return ctxerr.Wrapf(ctx, err, "deleting pack_targets for host %d", hid)
}
// no point trying the uuid-based tables if the host's uuid is missing
if hostUUID != "" {
for table, col := range additionalHostRefsByUUID {
if _, err := tx.ExecContext(ctx, fmt.Sprintf("DELETE FROM `%s` WHERE `%s`=?", table, col), hostUUID); err != nil {
return ctxerr.Wrapf(ctx, err, "deleting %s for host uuid %s", table, hostUUID)
}
}
}
return nil
})
}

View File

@ -5396,6 +5396,10 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
})
require.NoError(t, err)
require.NotNil(t, host)
// enroll in Fleet MDM
nanoEnroll(t, ds, host, false)
// Updates host_software.
software := []fleet.Software{
{Name: "foo", Version: "0.0.1", Source: "chrome_extensions"},
@ -5488,7 +5492,7 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.NoError(t, ds.RecordPolicyQueryExecutions(context.Background(), host, map[uint]*bool{policy.ID: ptr.Bool(true)}, time.Now(), false))
// Update host_mdm.
err = ds.SetOrUpdateMDMData(context.Background(), host.ID, false, false, "foo.mdm.example.com", false, "")
err = ds.SetOrUpdateMDMData(context.Background(), host.ID, false, true, "foo.mdm.example.com", false, "")
require.NoError(t, err)
// Update host_munki_info.
err = ds.SetOrUpdateMunkiInfo(context.Background(), host.ID, "42", []string{"a"}, []string{"b"})
@ -5515,6 +5519,13 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
// set an encryption key
err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host.ID, "TESTKEY")
require.NoError(t, err)
// set an mdm profile
prof, err := ds.NewMDMAppleConfigProfile(context.Background(), *configProfileForTest(t, "N1", "I1", "U1"))
require.NoError(t, err)
err = ds.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{ProfileID: prof.ProfileID, ProfileIdentifier: prof.Identifier, ProfileName: prof.Name, HostUUID: host.UUID, OperationType: fleet.MDMAppleOperationTypeInstall},
})
require.NoError(t, err)
// Operating system vulnerabilities
_, err = ds.writer.Exec(
@ -5527,9 +5538,15 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
for _, hostRef := range hostRefs {
var ok bool
err = ds.writer.Get(&ok, fmt.Sprintf("SELECT 1 FROM %s WHERE host_id = ?", hostRef), host.ID)
require.NoError(t, err)
require.NoError(t, err, hostRef)
require.True(t, ok, "table: %s", hostRef)
}
for tbl, col := range additionalHostRefsByUUID {
var ok bool
err = ds.writer.Get(&ok, fmt.Sprintf("SELECT 1 FROM %s WHERE %s = ?", tbl, col), host.UUID)
require.NoError(t, err, tbl)
require.True(t, ok, "table: %s", tbl)
}
err = ds.DeleteHosts(context.Background(), []uint{host.ID})
require.NoError(t, err)
@ -5541,6 +5558,12 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
require.True(t, err == nil || errors.Is(err, sql.ErrNoRows), "table: %s", hostRef)
require.False(t, ok, "table: %s", hostRef)
}
for tbl, col := range additionalHostRefsByUUID {
var ok bool
err = ds.writer.Get(&ok, fmt.Sprintf("SELECT 1 FROM %s WHERE %s = ?", tbl, col), host.UUID)
require.True(t, err == nil || errors.Is(err, sql.ErrNoRows), "table: %s", tbl)
require.False(t, ok, "table: %s", tbl)
}
}
func testHostIDsByOSVersion(t *testing.T, ds *Datastore) {