Added cleanup job to delete stuck pending Apple profiles (#24437)

#23816

This fix may not completely fix the customer's issue. However, I'd like
to see if there are improvements from this fix combined with the
previous query optimization fix.

# 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/Committing-Changes.md#changes-files)
for more information.
- [x] Added/updated tests
- [x] If database migrations are included, checked table schema to
confirm autoupdate
- For database migrations:
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Victor Lyuboslavsky
2024-12-05 15:40:59 -06:00
committed by GitHub
parent d1425fc83b
commit 968f329725
8 changed files with 78 additions and 2 deletions
+1
View File
@@ -0,0 +1 @@
Added cleanup job to delete stuck pending Apple profiles, and requeue them.
+3
View File
@@ -964,6 +964,9 @@ func newCleanupsAndAggregationSchedule(
schedule.WithJob("cleanup_host_mdm_managed_certificates", func(ctx context.Context) error {
return ds.CleanUpMDMManagedCertificates(ctx)
}),
schedule.WithJob("cleanup_host_mdm_apple_profiles", func(ctx context.Context) error {
return ds.CleanupHostMDMAppleProfiles(ctx)
}),
)
return s, nil
+15
View File
@@ -5712,6 +5712,21 @@ func (ds *Datastore) CleanupHostMDMCommands(ctx context.Context) error {
return nil
}
func (ds *Datastore) CleanupHostMDMAppleProfiles(ctx context.Context) error {
// Delete pending commands that don't have a corresponding entry in nano_enrollment_queue.
// This could occur due to errors (i.e., large server/DB load) or server being stopped while processing the profiles.
// After the entry is deleted, the mdm_apple_profile_manager job will try to requeue the profile.
stmt := fmt.Sprintf(`
DELETE hmap FROM host_mdm_apple_profiles AS hmap
LEFT JOIN nano_enrollment_queue neq ON hmap.host_uuid = neq.id AND hmap.command_uuid = neq.command_uuid
WHERE neq.id IS NULL AND (hmap.status IS NULL OR hmap.status = '%s') AND hmap.updated_at < NOW() - INTERVAL 1 HOUR`,
fleet.MDMDeliveryPending)
if _, err := ds.writer(ctx).ExecContext(ctx, stmt); err != nil {
return ctxerr.Wrap(ctx, err, "delete from host_mdm_apple_profiles")
}
return nil
}
func (ds *Datastore) GetMDMAppleOSUpdatesSettingsByHostSerial(ctx context.Context, serial string) (*fleet.AppleOSUpdateSettings, error) {
stmt := `
SELECT
+19
View File
@@ -571,6 +571,10 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
})
require.NoError(t, err)
// The pending profile will be NOT be cleaned up because it was updated too recently.
err = ds.CleanupHostMDMAppleProfiles(ctx)
require.NoError(t, err)
gotProfs, err = ds.GetHostMDMAppleProfiles(ctx, h1.UUID)
require.NoError(t, err)
require.Len(t, gotProfs, 2) // remove+verifying is not there anymore
@@ -588,6 +592,21 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
require.Equal(t, ep.OperationType, gp.OperationType)
require.Equal(t, ep.Detail, gp.Detail)
}
// Update the timestamps of the profiles
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `UPDATE host_mdm_apple_profiles SET updated_at = updated_at - INTERVAL 2 HOUR`)
return err
})
// The pending profile will be cleaned up because we did not populate the corresponding nano table in this test.
err = ds.CleanupHostMDMAppleProfiles(ctx)
require.NoError(t, err)
gotProfs, err = ds.GetHostMDMAppleProfiles(ctx, h1.UUID)
require.NoError(t, err)
require.Len(t, gotProfs, 1)
assert.Equal(t, &fleet.MDMDeliveryVerifying, gotProfs[0].Status)
}
func TestIngestMDMAppleDevicesFromDEPSync(t *testing.T) {
@@ -0,0 +1,22 @@
package tables
import (
"database/sql"
)
func init() {
MigrationClient.AddMigration(Up_20241205122800, Down_20241205122800)
}
func Up_20241205122800(tx *sql.Tx) error {
_, err := tx.Exec(
"ALTER TABLE host_mdm_apple_profiles " +
"ADD COLUMN created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), " +
"ADD COLUMN updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)",
)
return err
}
func Down_20241205122800(_ *sql.Tx) error {
return nil
}
File diff suppressed because one or more lines are too long
+2
View File
@@ -346,6 +346,8 @@ type Datastore interface {
RemoveHostMDMCommand(ctx context.Context, command HostMDMCommand) error
// CleanupHostMDMCommands removes invalid and stale MDM commands sent to hosts.
CleanupHostMDMCommands(ctx context.Context) error
// CleanupHostMDMAppleProfiles removes abandoned host MDM Apple profiles entries.
CleanupHostMDMAppleProfiles(ctx context.Context) error
// IsHostConnectedToFleetMDM verifies if the host has an active Fleet MDM enrollment with this server
IsHostConnectedToFleetMDM(ctx context.Context, host *Host) (bool, error)
+12
View File
@@ -261,6 +261,8 @@ type RemoveHostMDMCommandFunc func(ctx context.Context, command fleet.HostMDMCom
type CleanupHostMDMCommandsFunc func(ctx context.Context) error
type CleanupHostMDMAppleProfilesFunc func(ctx context.Context) error
type IsHostConnectedToFleetMDMFunc func(ctx context.Context, host *fleet.Host) (bool, error)
type AreHostsConnectedToFleetMDMFunc func(ctx context.Context, hosts []*fleet.Host) (map[string]bool, error)
@@ -1534,6 +1536,9 @@ type DataStore struct {
CleanupHostMDMCommandsFunc CleanupHostMDMCommandsFunc
CleanupHostMDMCommandsFuncInvoked bool
CleanupHostMDMAppleProfilesFunc CleanupHostMDMAppleProfilesFunc
CleanupHostMDMAppleProfilesFuncInvoked bool
IsHostConnectedToFleetMDMFunc IsHostConnectedToFleetMDMFunc
IsHostConnectedToFleetMDMFuncInvoked bool
@@ -3745,6 +3750,13 @@ func (s *DataStore) CleanupHostMDMCommands(ctx context.Context) error {
return s.CleanupHostMDMCommandsFunc(ctx)
}
func (s *DataStore) CleanupHostMDMAppleProfiles(ctx context.Context) error {
s.mu.Lock()
s.CleanupHostMDMAppleProfilesFuncInvoked = true
s.mu.Unlock()
return s.CleanupHostMDMAppleProfilesFunc(ctx)
}
func (s *DataStore) IsHostConnectedToFleetMDM(ctx context.Context, host *fleet.Host) (bool, error) {
s.mu.Lock()
s.IsHostConnectedToFleetMDMFuncInvoked = true