Remove "do not enqueue setup experience items >24 hours after enrollment" logic for macOS hosts (#40739)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* Fixed a bug where macOS systems previous enrolled in fleet wouldn't always go through setup experience after a wipe
|
||||
@@ -23,32 +23,34 @@ func (ds *Datastore) ResetSetupExperienceItemsAfterFailure(ctx context.Context,
|
||||
}
|
||||
|
||||
func (ds *Datastore) enqueueSetupExperienceItems(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint, resetFailedSetupSteps bool) (bool, error) {
|
||||
// Find the host with the given UUID and platform. If it's already been enrolled for > the cutoff,
|
||||
// don't enqueue any items. This handles the edge case where an enrolled host upgrades from an
|
||||
// Orbit version that didn't support setup experience to one that does.
|
||||
// See https://github.com/fleetdm/fleet/issues/35717
|
||||
stmtHost := `
|
||||
SELECT
|
||||
last_enrolled_at
|
||||
FROM
|
||||
hosts
|
||||
WHERE uuid = ? AND platform = ?
|
||||
`
|
||||
var lastEnrolledAt sql.NullTime
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &lastEnrolledAt, stmtHost, hostUUID, hostPlatformLike); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// This shouldn't happen but we don't check for it elsewhere,
|
||||
// so we'll log a warning and continue.
|
||||
ds.logger.WarnContext(ctx, "Host not found while enqueueing setup experience items", "host_uuid", hostUUID, "platform_like", hostPlatformLike)
|
||||
} else {
|
||||
return false, ctxerr.Wrap(ctx, err, "finding host for enqueueing setup experience items")
|
||||
if hostPlatformLike != "darwin" && hostPlatformLike != "ios" && hostPlatformLike != "ipados" {
|
||||
// Find the host with the given UUID and platform. If it's already been enrolled for > the cutoff,
|
||||
// don't enqueue any items. This handles the edge case where an enrolled host upgrades from an
|
||||
// Orbit version that didn't support setup experience to one that does.
|
||||
// See https://github.com/fleetdm/fleet/issues/35717
|
||||
stmtHost := `
|
||||
SELECT
|
||||
last_enrolled_at
|
||||
FROM
|
||||
hosts
|
||||
WHERE uuid = ? AND platform = ?
|
||||
`
|
||||
var lastEnrolledAt sql.NullTime
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &lastEnrolledAt, stmtHost, hostUUID, hostPlatformLike); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// This shouldn't happen but we don't check for it elsewhere,
|
||||
// so we'll log a warning and continue.
|
||||
ds.logger.WarnContext(ctx, "Host not found while enqueueing setup experience items", "host_uuid", hostUUID, "platform_like", hostPlatformLike)
|
||||
} else {
|
||||
return false, ctxerr.Wrap(ctx, err, "finding host for enqueueing setup experience items")
|
||||
}
|
||||
}
|
||||
// If the host was enrolled more than 24 hours ago, don't enqueue any items.
|
||||
// Note: if the last enroll date is our "zero date" (1/1/2000), treat it as if it's never enrolled.
|
||||
if lastEnrolledAt.Valid && lastEnrolledAt.Time.Before(time.Now().Add(-24*time.Hour)) && lastEnrolledAt.Time.After(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)) {
|
||||
ds.logger.DebugContext(ctx, "Host enrolled more than 24 hours ago, skipping enqueueing setup experience items", "host_uuid", hostUUID, "platform_like", hostPlatformLike, "last_enrolled_at", lastEnrolledAt.Time)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
// If the host was enrolled more than 24 hours ago, don't enqueue any items.
|
||||
// Note: if the last enroll date is our "zero date" (1/1/2000), treat it as if it's never enrolled.
|
||||
if lastEnrolledAt.Valid && lastEnrolledAt.Time.Before(time.Now().Add(-24*time.Hour)) && lastEnrolledAt.Time.After(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)) {
|
||||
ds.logger.DebugContext(ctx, "Host enrolled more than 24 hours ago, skipping enqueueing setup experience items", "host_uuid", hostUUID, "platform_like", hostPlatformLike, "last_enrolled_at", lastEnrolledAt.Time)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// NOTE: currently, the Android platform does not use the "enqueue setup experience items" flow as it
|
||||
|
||||
@@ -32,6 +32,7 @@ func TestSetupExperience(t *testing.T) {
|
||||
{"TestHostInSetupExperience", testHostInSetupExperience},
|
||||
{"TestGetSetupExperienceScriptByID", testGetSetupExperienceScriptByID},
|
||||
{"TestUpdateSetupExperienceScriptWhileEnqueued", testUpdateSetupExperienceScriptWhileEnqueued},
|
||||
{"TestEnqueueSetupExperienceItemsWindows", testEnqueueSetupExperienceItemsWindows},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -230,6 +231,103 @@ func testEnqueueSetupExperienceLinuxScriptPackages(t *testing.T, ds *Datastore)
|
||||
})
|
||||
}
|
||||
|
||||
func testEnqueueSetupExperienceItemsWindows(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1"})
|
||||
require.NoError(t, err)
|
||||
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "team2"})
|
||||
require.NoError(t, err)
|
||||
|
||||
user1 := test.NewUser(t, ds, "Alice", "alice@example.com", true)
|
||||
|
||||
// Create some software installers and add them to setup experience
|
||||
tfr1, err := fleet.NewTempFileReader(strings.NewReader("hello"), t.TempDir)
|
||||
require.NoError(t, err)
|
||||
installerID1, _, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
||||
InstallScript: "hello",
|
||||
PreInstallQuery: "SELECT 1",
|
||||
PostInstallScript: "world",
|
||||
UninstallScript: "goodbye",
|
||||
InstallerFile: tfr1,
|
||||
StorageID: "storage1",
|
||||
Filename: "file1",
|
||||
Title: "Software1",
|
||||
Version: "1.0",
|
||||
Source: "apps",
|
||||
UserID: user1.ID,
|
||||
TeamID: &team1.ID,
|
||||
Platform: "windows",
|
||||
ValidatedLabels: &fleet.LabelIdentsWithScope{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
tfr2, err := fleet.NewTempFileReader(strings.NewReader("hello"), t.TempDir)
|
||||
require.NoError(t, err)
|
||||
installerID2, _, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
||||
InstallScript: "banana",
|
||||
PreInstallQuery: "SELECT 3",
|
||||
PostInstallScript: "apple",
|
||||
InstallerFile: tfr2,
|
||||
StorageID: "storage3",
|
||||
Filename: "file3",
|
||||
Title: "Software2",
|
||||
Version: "3.0",
|
||||
Source: "apps",
|
||||
SelfService: true,
|
||||
UserID: user1.ID,
|
||||
TeamID: &team2.ID,
|
||||
Platform: "windows",
|
||||
ValidatedLabels: &fleet.LabelIdentsWithScope{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, "UPDATE software_installers SET install_during_setup = 1 WHERE id IN (?, ?)", installerID1, installerID2)
|
||||
return err
|
||||
})
|
||||
|
||||
host1UUID := "11111111-1111-1111-1111-111111111111"
|
||||
host2UUID := "22222222-2222-2222-2222-222222222222"
|
||||
|
||||
// Freshly enrolled host, should get items enqueued
|
||||
_, err = ds.NewHost(ctx, &fleet.Host{
|
||||
Hostname: "windows-test-1",
|
||||
OsqueryHostID: ptr.String("osquery-windows-1"),
|
||||
NodeKey: ptr.String("node-key-windows-1"),
|
||||
UUID: host1UUID,
|
||||
Platform: "windows",
|
||||
HardwareSerial: "654321a-1",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, "UPDATE hosts SET last_enrolled_at = ? WHERE uuid = ?", time.Now().Add(-1*time.Hour), host1UUID)
|
||||
return err
|
||||
})
|
||||
|
||||
// Enroll date > 24 hours ago and is windows. This should not get items enqueued.
|
||||
_, err = ds.NewHost(ctx, &fleet.Host{
|
||||
Hostname: "windows-test-2",
|
||||
OsqueryHostID: ptr.String("osquery-windows-2"),
|
||||
NodeKey: ptr.String("node-key-windows-2"),
|
||||
UUID: host2UUID,
|
||||
Platform: "windows",
|
||||
HardwareSerial: "654321b-2",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, "UPDATE hosts SET last_enrolled_at = ? WHERE uuid = ?", time.Now().Add(-25*time.Hour), host2UUID)
|
||||
return err
|
||||
})
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "windows", host1UUID, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "windows", host2UUID, team2.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, anythingEnqueued)
|
||||
}
|
||||
|
||||
func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
test.CreateInsertGlobalVPPToken(t, ds)
|
||||
@@ -352,7 +450,7 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
// Deliberately not adding a record for the hostTeam2Missing, to verify that
|
||||
// we still enqueue items for it if it doesn't exist in the database.
|
||||
|
||||
// Enroll date > 24 hours ago. This should NOT have items enqueued.
|
||||
// Enroll date > 24 hours ago but is macOS. This should get items enqueued.
|
||||
_, err = ds.NewHost(ctx, &fleet.Host{
|
||||
Hostname: "macos-test-4",
|
||||
OsqueryHostID: ptr.String("osquery-macos-4"),
|
||||
@@ -422,12 +520,11 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam1Old, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, anythingEnqueued)
|
||||
// This host enrolled > 24 hours ago, so we shouldn't enqueue any items for it.
|
||||
require.True(t, anythingEnqueued)
|
||||
// This host enrolled > 24 hours ago, but it's darwin, so we should enqueue items for it.
|
||||
awaitingConfig, err = ds.GetHostAwaitingConfiguration(ctx, hostTeam1Old)
|
||||
require.Error(t, err)
|
||||
require.True(t, fleet.IsNotFound(err))
|
||||
require.False(t, awaitingConfig)
|
||||
require.NoError(t, err)
|
||||
require.True(t, awaitingConfig)
|
||||
|
||||
seRows := []setupExperienceInsertTestRows{}
|
||||
|
||||
@@ -435,8 +532,8 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
return sqlx.SelectContext(ctx, q, &seRows, "SELECT host_uuid, name, status, software_installer_id, setup_experience_script_id, vpp_app_team_id FROM setup_experience_status_results")
|
||||
})
|
||||
|
||||
// four hosts with three items enqueued each.
|
||||
require.Len(t, seRows, 12)
|
||||
// five hosts with three items enqueued each.
|
||||
require.Len(t, seRows, 15)
|
||||
|
||||
for _, tc := range []setupExperienceInsertTestRows{
|
||||
{
|
||||
@@ -525,9 +622,9 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
return sqlx.SelectContext(ctx, q, &seRows, "SELECT host_uuid, name, status, software_installer_id, setup_experience_script_id, vpp_app_team_id FROM setup_experience_status_results")
|
||||
})
|
||||
|
||||
// Only the team 1 host should have items enqueued now.
|
||||
// Only the team 1 and team 3 hosts should have items enqueued now.
|
||||
// Two hosts with three items each.
|
||||
require.Len(t, seRows, 6)
|
||||
require.Len(t, seRows, 9)
|
||||
|
||||
for _, tc := range []setupExperienceInsertTestRows{
|
||||
{
|
||||
@@ -562,11 +659,8 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
||||
}
|
||||
|
||||
for _, row := range seRows {
|
||||
if row.HostUUID == hostTeam3 || row.HostUUID == hostTeam2 {
|
||||
if row.HostUUID == hostTeam2 {
|
||||
team := 2
|
||||
if row.HostUUID == hostTeam3 {
|
||||
team = 3
|
||||
}
|
||||
t.Errorf("team %d shouldn't have any any entries", team)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user