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:
@@ -0,0 +1 @@
|
||||
Added cleanup job to delete stuck pending Apple profiles, and requeue them.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+22
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user