mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Bugfix: properly enqueue compatible setup experience items for arch/omarchy linux (#41778)
This commit is contained in:
parent
5a266bfaca
commit
8c85ef8ad3
13 changed files with 189 additions and 48 deletions
|
|
@ -0,0 +1 @@
|
|||
- Fixed an issue where setup experience items (software to install) were not enqueued for Linux distributions that did not report a "platform-like" value, e.g. Arch Linux and Omarchy.
|
||||
|
|
@ -160,11 +160,7 @@ func (svc *Service) GetOrbitSetupExperienceStatus(ctx context.Context, orbitNode
|
|||
// If so, call the enqueue function with a flag to retain successful steps.
|
||||
if requireAllSoftware {
|
||||
svc.logger.InfoContext(ctx, "re-enqueueing cancelled setup experience steps after a previous software install failure", "host_uuid", host.UUID)
|
||||
platform := host.PlatformLike
|
||||
if platform == "" {
|
||||
platform = host.Platform
|
||||
}
|
||||
_, err := svc.ds.ResetSetupExperienceItemsAfterFailure(ctx, platform, host.UUID, teamID)
|
||||
_, err := svc.ds.ResetSetupExperienceItemsAfterFailure(ctx, host.Platform, host.PlatformLike, host.UUID, teamID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "re-enqueueing cancelled setup experience steps after a previous software install failure")
|
||||
}
|
||||
|
|
@ -390,7 +386,7 @@ func (svc *Service) SetupExperienceInit(ctx context.Context) (*fleet.SetupExperi
|
|||
return nil, ctxerr.Wrap(ctx, err, "failed to get host's UUID for the setup experience")
|
||||
}
|
||||
|
||||
enabled, err := svc.ds.EnqueueSetupExperienceItems(ctx, host.PlatformLike, hostUUID, teamID)
|
||||
enabled, err := svc.ds.EnqueueSetupExperienceItems(ctx, host.Platform, host.PlatformLike, hostUUID, teamID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "check for software titles for setup experience")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8820,7 +8820,7 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
|
|||
err = ds.SetSetupExperienceScript(ctx, &fleet.Script{Name: "test.sh", ScriptContents: "echo foo"})
|
||||
require.NoError(t, err)
|
||||
|
||||
added, err := ds.EnqueueSetupExperienceItems(ctx, host.Platform, host.UUID, 0)
|
||||
added, err := ds.EnqueueSetupExperienceItems(ctx, host.Platform, host.PlatformLike, host.UUID, 0)
|
||||
require.NoError(t, err)
|
||||
require.True(t, added)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,15 +14,44 @@ import (
|
|||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func (ds *Datastore) EnqueueSetupExperienceItems(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
return ds.enqueueSetupExperienceItems(ctx, hostPlatformLike, hostUUID, teamID, false)
|
||||
func (ds *Datastore) EnqueueSetupExperienceItems(ctx context.Context, hostPlatform, hostPlatformLike, hostUUID string, teamID uint) (bool, error) {
|
||||
return ds.enqueueSetupExperienceItems(ctx, hostPlatform, hostPlatformLike, hostUUID, teamID, false)
|
||||
}
|
||||
|
||||
func (ds *Datastore) ResetSetupExperienceItemsAfterFailure(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
return ds.enqueueSetupExperienceItems(ctx, hostPlatformLike, hostUUID, teamID, true)
|
||||
func (ds *Datastore) ResetSetupExperienceItemsAfterFailure(ctx context.Context, hostPlatform, hostPlatformLike, hostUUID string, teamID uint) (bool, error) {
|
||||
return ds.enqueueSetupExperienceItems(ctx, hostPlatform, hostPlatformLike, hostUUID, teamID, true)
|
||||
}
|
||||
|
||||
func (ds *Datastore) enqueueSetupExperienceItems(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint, resetFailedSetupSteps bool) (bool, error) {
|
||||
func (ds *Datastore) enqueueSetupExperienceItems(ctx context.Context, hostPlatform, hostPlatformLike, hostUUID string, teamID uint, resetFailedSetupSteps bool) (bool, error) {
|
||||
// NOTE: there are 3 different "platform" values in play here: host platform,
|
||||
// host platform-like and fleet-platform-like.
|
||||
//
|
||||
// The host platform is the most specific, e.g. "darwin", "windows", "ios",
|
||||
// "ubuntu", "arch", "fedora", etc.
|
||||
//
|
||||
// Platform-like is the "generic platform" to which the specific platform belongs,
|
||||
// e.g. "debian" for "ubuntu", "rhel" for "fedora", etc. For Apple or Windows, it
|
||||
// is typically the same as platform. It may be empty in some cases (e.g. for "arch"
|
||||
// as it doesn't have a "ID_LIKE" set in /etc/os-release by default, but also "ios").
|
||||
//
|
||||
// Fleet-platform-like is the even-more-generic platform, and is implemented in
|
||||
// fleet.PlatformFromHost: "windows", "darwin", "linux", "ios", etc.
|
||||
//
|
||||
// So for many platforms, all three are the same, but for linux distros, those can be
|
||||
// 3 different values. There is no harm - at least in this function - in filling
|
||||
// hostPlatformLike to hostPlatform if it is empty (e.g. for "ios" or "arch").
|
||||
//
|
||||
// From my tests enrolling such hosts, results are:
|
||||
// - host platform - host platform like - fleet platform like -
|
||||
// ios <empty> ios
|
||||
// darwin darwin darwin
|
||||
// arch <empty> linux
|
||||
// ubuntu debian linux
|
||||
// windows windows windows
|
||||
if hostPlatformLike == "" {
|
||||
hostPlatformLike = hostPlatform
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -36,11 +65,11 @@ func (ds *Datastore) enqueueSetupExperienceItems(ctx context.Context, hostPlatfo
|
|||
WHERE uuid = ? AND platform = ?
|
||||
`
|
||||
var lastEnrolledAt sql.NullTime
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &lastEnrolledAt, stmtHost, hostUUID, hostPlatformLike); err != nil {
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &lastEnrolledAt, stmtHost, hostUUID, hostPlatform); 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)
|
||||
ds.logger.WarnContext(ctx, "Host not found while enqueueing setup experience items", "host_uuid", hostUUID, "platform_like", hostPlatformLike, "platform", hostPlatform)
|
||||
} else {
|
||||
return false, ctxerr.Wrap(ctx, err, "finding host for enqueueing setup experience items")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ func testEnqueueSetupExperienceLinuxScriptPackages(t *testing.T, ds *Datastore)
|
|||
return err
|
||||
})
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "debian", hostDebianShOnly, team1.ID)
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "ubuntu", "debian", hostDebianShOnly, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued, "BUG #34654: .sh package alone should trigger setup experience")
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ func testEnqueueSetupExperienceLinuxScriptPackages(t *testing.T, ds *Datastore)
|
|||
return err
|
||||
})
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "rhel", hostRhelShOnly, team1.ID)
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "fedora", "rhel", hostRhelShOnly, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued, "BUG #34654: .sh package should work on RHEL too")
|
||||
|
||||
|
|
@ -189,7 +189,7 @@ func testEnqueueSetupExperienceLinuxScriptPackages(t *testing.T, ds *Datastore)
|
|||
t.Run("mixed_sh_deb_debian", func(t *testing.T) {
|
||||
hostDebianMixed := "debian-mixed-" + uuid.NewString()
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "debian", hostDebianMixed, team1.ID)
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "ubuntu", "debian", hostDebianMixed, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ func testEnqueueSetupExperienceLinuxScriptPackages(t *testing.T, ds *Datastore)
|
|||
t.Run("mixed_sh_deb_rhel", func(t *testing.T) {
|
||||
hostRhelMixed := "rhel-mixed-" + uuid.NewString()
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "rhel", hostRhelMixed, team1.ID)
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "fedora", "rhel", hostRhelMixed, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
|
|
@ -319,11 +319,11 @@ func testEnqueueSetupExperienceItemsWindows(t *testing.T, ds *Datastore) {
|
|||
return err
|
||||
})
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "windows", host1UUID, team1.ID)
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "windows", "windows", host1UUID, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "windows", host2UUID, team2.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "windows", "windows", host2UUID, team2.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, anythingEnqueued)
|
||||
}
|
||||
|
|
@ -480,35 +480,35 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
|||
return err
|
||||
})
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam1, team1.ID)
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam1, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
awaitingConfig, err := ds.GetHostAwaitingConfiguration(ctx, hostTeam1)
|
||||
require.NoError(t, err)
|
||||
require.True(t, awaitingConfig)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam1New, team1.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam1New, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
awaitingConfig, err = ds.GetHostAwaitingConfiguration(ctx, hostTeam1New)
|
||||
require.NoError(t, err)
|
||||
require.True(t, awaitingConfig)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam2, team2.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam2, team2.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
awaitingConfig, err = ds.GetHostAwaitingConfiguration(ctx, hostTeam2)
|
||||
require.NoError(t, err)
|
||||
require.True(t, awaitingConfig)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam2Missing, team2.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam2Missing, team2.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
awaitingConfig, err = ds.GetHostAwaitingConfiguration(ctx, hostTeam2Missing)
|
||||
require.NoError(t, err)
|
||||
require.True(t, awaitingConfig)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam3, team3.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam3, team3.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, anythingEnqueued)
|
||||
// Nothing is configured for setup experience in team 3, so we do not set
|
||||
|
|
@ -518,7 +518,7 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
|||
require.True(t, fleet.IsNotFound(err))
|
||||
require.False(t, awaitingConfig)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam1Old, team1.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam1Old, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
// This host enrolled > 24 hours ago, but it's darwin, so we should enqueue items for it.
|
||||
|
|
@ -602,19 +602,19 @@ func testEnqueueSetupExperienceItems(t *testing.T, ds *Datastore) {
|
|||
err = ds.SetSetupExperienceSoftwareTitles(ctx, "darwin", team2.ID, []uint{})
|
||||
require.NoError(t, err)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam1, team1.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam1, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
// team2 now has nothing enqueued
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam2, team2.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam2, team2.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, anythingEnqueued)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam2Missing, team2.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam2Missing, team2.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, anythingEnqueued)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam3, team3.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam3, team3.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, anythingEnqueued)
|
||||
|
||||
|
|
@ -1431,11 +1431,11 @@ func testUpdateSetupExperienceScriptWhileEnqueued(t *testing.T, ds *Datastore) {
|
|||
hostTeam1UUID := "123"
|
||||
hostTeam2UUID := "456"
|
||||
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam1UUID, team1.ID)
|
||||
anythingEnqueued, err := ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam1UUID, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam2UUID, team2.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam2UUID, team2.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
|
|
@ -1495,7 +1495,7 @@ func testUpdateSetupExperienceScriptWhileEnqueued(t *testing.T, ds *Datastore) {
|
|||
require.Equal(t, team2OriginalScript.ID, *host2NewItems[0].SetupExperienceScriptID)
|
||||
|
||||
// re-enqueue items for host 1, should enqueue the updated script
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", hostTeam1UUID, team1.ID)
|
||||
anythingEnqueued, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", hostTeam1UUID, team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, anythingEnqueued)
|
||||
|
||||
|
|
|
|||
|
|
@ -1747,7 +1747,7 @@ func testBatchSetSoftwareInstallersSetupExperienceSideEffects(t *testing.T, ds *
|
|||
})
|
||||
|
||||
// Add setup_experience_status_results for both installers
|
||||
_, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", host1.UUID, *host1.TeamID)
|
||||
_, err = ds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", host1.UUID, *host1.TeamID)
|
||||
require.NoError(t, err)
|
||||
|
||||
statuses, err := ds.ListSetupExperienceResultsByHostUUID(ctx, host1.UUID)
|
||||
|
|
|
|||
|
|
@ -2340,12 +2340,13 @@ type Datastore interface {
|
|||
// It uses hostPlatformLike to cover scenarios where software items are not compatible with the target
|
||||
// platform. E.g. "deb" packages can only be queued for hosts with platform_like = "debian" (Ubuntu, Debian, etc.).
|
||||
// MacOS hosts have hosts.platform_like = 'darwin', Ubuntu and Debian hosts have hosts.platform_like = 'debian'
|
||||
// Fedora hosts have hosts.platform_like = 'rhel'.
|
||||
EnqueueSetupExperienceItems(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error)
|
||||
// Fedora hosts have hosts.platform_like = 'rhel'. The hostPlatform argument (e.g. "darwin", "arch", "ubuntu", etc.)
|
||||
// is used for some validations, and to backfill hostPlatformLike if empty.
|
||||
EnqueueSetupExperienceItems(ctx context.Context, hostPlatform, hostPlatformLike, hostUUID string, teamID uint) (bool, error)
|
||||
|
||||
// ResetSetupExperienceItemsAfterFailure resets any setup experience items that were canceled after
|
||||
// a software item failed to install on a host whose team was configured to stop setup experience on failure.
|
||||
ResetSetupExperienceItemsAfterFailure(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error)
|
||||
ResetSetupExperienceItemsAfterFailure(ctx context.Context, hostPlatform, hostPlatformLike, hostUUID string, teamID uint) (bool, error)
|
||||
|
||||
// CancelPendingSetupExperienceSteps cancels any setup experience items for the given host that aren't already completed.
|
||||
CancelPendingSetupExperienceSteps(ctx context.Context, hostUUID string) error
|
||||
|
|
|
|||
|
|
@ -1499,9 +1499,9 @@ type ListSetupExperienceResultsByHostUUIDFunc func(ctx context.Context, hostUUID
|
|||
|
||||
type UpdateSetupExperienceStatusResultFunc func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error
|
||||
|
||||
type EnqueueSetupExperienceItemsFunc func(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error)
|
||||
type EnqueueSetupExperienceItemsFunc func(ctx context.Context, hostPlatform string, hostPlatformLike string, hostUUID string, teamID uint) (bool, error)
|
||||
|
||||
type ResetSetupExperienceItemsAfterFailureFunc func(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error)
|
||||
type ResetSetupExperienceItemsAfterFailureFunc func(ctx context.Context, hostPlatform string, hostPlatformLike string, hostUUID string, teamID uint) (bool, error)
|
||||
|
||||
type CancelPendingSetupExperienceStepsFunc func(ctx context.Context, hostUUID string) error
|
||||
|
||||
|
|
@ -9673,18 +9673,18 @@ func (s *DataStore) UpdateSetupExperienceStatusResult(ctx context.Context, statu
|
|||
return s.UpdateSetupExperienceStatusResultFunc(ctx, status)
|
||||
}
|
||||
|
||||
func (s *DataStore) EnqueueSetupExperienceItems(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
func (s *DataStore) EnqueueSetupExperienceItems(ctx context.Context, hostPlatform string, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
s.mu.Lock()
|
||||
s.EnqueueSetupExperienceItemsFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.EnqueueSetupExperienceItemsFunc(ctx, hostPlatformLike, hostUUID, teamID)
|
||||
return s.EnqueueSetupExperienceItemsFunc(ctx, hostPlatform, hostPlatformLike, hostUUID, teamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) ResetSetupExperienceItemsAfterFailure(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
func (s *DataStore) ResetSetupExperienceItemsAfterFailure(ctx context.Context, hostPlatform string, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
s.mu.Lock()
|
||||
s.ResetSetupExperienceItemsAfterFailureFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.ResetSetupExperienceItemsAfterFailureFunc(ctx, hostPlatformLike, hostUUID, teamID)
|
||||
return s.ResetSetupExperienceItemsAfterFailureFunc(ctx, hostPlatform, hostPlatformLike, hostUUID, teamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) CancelPendingSetupExperienceSteps(ctx context.Context, hostUUID string) error {
|
||||
|
|
|
|||
|
|
@ -3495,7 +3495,9 @@ func (svc *MDMAppleCheckinAndCommandService) TokenUpdate(r *mdm.Request, m *mdm.
|
|||
// We do check the license before actually _running_ setup experience items.
|
||||
if enqueueSetupExperienceItems {
|
||||
// Enqueue setup experience items and mark the host as being in setup experience
|
||||
hasSetupExpItems, err = svc.ds.EnqueueSetupExperienceItems(r.Context, info.Platform, r.ID, info.TeamID)
|
||||
// NOTE: we don't have PlatformLike field for `info`, but that's fine as this is Apple-specific
|
||||
// flow and the platform is always the same as platform-like.
|
||||
hasSetupExpItems, err = svc.ds.EnqueueSetupExperienceItems(r.Context, info.Platform, info.Platform, r.ID, info.TeamID)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(r.Context, err, "queueing setup experience tasks")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1670,7 +1670,7 @@ func TestMDMTokenUpdate(t *testing.T) {
|
|||
require.True(t, newActivityFuncInvoked)
|
||||
|
||||
// With AwaitingConfiguration - should check for and enqueue SetupExperience items
|
||||
ds.EnqueueSetupExperienceItemsFunc = func(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
ds.EnqueueSetupExperienceItemsFunc = func(ctx context.Context, hostPlatform, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
require.Equal(t, "darwin", hostPlatformLike)
|
||||
require.Equal(t, uuid, hostUUID)
|
||||
require.Equal(t, wantTeamID, teamID)
|
||||
|
|
@ -1808,7 +1808,7 @@ func TestMDMTokenUpdateIOS(t *testing.T) {
|
|||
return &fleet.NanoEnrollment{Enabled: true, Type: "Device", TokenUpdateTally: 1}, nil
|
||||
}
|
||||
|
||||
ds.EnqueueSetupExperienceItemsFunc = func(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
ds.EnqueueSetupExperienceItemsFunc = func(ctx context.Context, hostPlatform, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
require.Equal(t, "ios", hostPlatformLike)
|
||||
require.Equal(t, uuid, hostUUID)
|
||||
require.Equal(t, wantTeamID, teamID)
|
||||
|
|
@ -6948,7 +6948,7 @@ func TestMDMTokenUpdateSCEPRenewal(t *testing.T) {
|
|||
scepRenewalInProgress = false
|
||||
return nil
|
||||
}
|
||||
ds.EnqueueSetupExperienceItemsFunc = func(ctx context.Context, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
ds.EnqueueSetupExperienceItemsFunc = func(ctx context.Context, hostPlatform, hostPlatformLike string, hostUUID string, teamID uint) (bool, error) {
|
||||
require.Equal(t, "darwin", hostPlatformLike)
|
||||
require.Equal(t, uuid, hostUUID)
|
||||
require.Equal(t, wantTeamID, teamID)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -4877,3 +4878,111 @@ func (s *integrationMDMTestSuite) createAndEnrollAndroidDevice(t *testing.T, nam
|
|||
|
||||
return hostResp.Host, deviceInfo, pubsubToken
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestLinuxSetupExperienceEnqueueSoftwareInstalls() {
|
||||
t := s.T()
|
||||
ctx := context.Background()
|
||||
s.setSkipWorkerJobs(t)
|
||||
|
||||
// add a macOS software to install
|
||||
payloadDummy := &fleet.UploadSoftwareInstallerPayload{
|
||||
Filename: "dummy_installer.pkg",
|
||||
Title: "DummyApp",
|
||||
TeamID: nil,
|
||||
}
|
||||
s.uploadSoftwareInstaller(t, payloadDummy, http.StatusOK, "")
|
||||
macTitleID := getSoftwareTitleID(t, s.ds, payloadDummy.Title, "apps")
|
||||
require.NotZero(t, macTitleID)
|
||||
|
||||
// add a .deb custom package
|
||||
payloadRuby := &fleet.UploadSoftwareInstallerPayload{
|
||||
Filename: "ruby.deb",
|
||||
Title: "ruby",
|
||||
Platform: "linux",
|
||||
TeamID: nil,
|
||||
}
|
||||
s.uploadSoftwareInstaller(t, payloadRuby, http.StatusOK, "")
|
||||
debTitleID := getSoftwareTitleID(t, s.ds, payloadRuby.Title, "deb_packages")
|
||||
require.NotZero(t, debTitleID)
|
||||
|
||||
// add a .sh script-only package
|
||||
payloadSh := &fleet.UploadSoftwareInstallerPayload{
|
||||
Filename: "script.sh",
|
||||
Title: "script",
|
||||
Platform: "linux",
|
||||
TeamID: nil,
|
||||
}
|
||||
s.uploadSoftwareInstaller(t, payloadSh, http.StatusOK, "")
|
||||
shTitleID := getSoftwareTitleID(t, s.ds, payloadSh.Title, "sh_packages")
|
||||
require.NotZero(t, shTitleID)
|
||||
|
||||
var putSetupExpResponse putSetupExperienceSoftwareResponse
|
||||
s.DoJSON("PUT", "/api/v1/fleet/setup_experience/software", putSetupExperienceSoftwareRequest{
|
||||
TeamID: 0, Platform: "linux", TitleIDs: []uint{debTitleID, shTitleID},
|
||||
}, http.StatusOK, &putSetupExpResponse)
|
||||
s.DoJSON("PUT", "/api/v1/fleet/setup_experience/software", putSetupExperienceSoftwareRequest{
|
||||
TeamID: 0, Platform: "macos", TitleIDs: []uint{macTitleID},
|
||||
}, http.StatusOK, &putSetupExpResponse)
|
||||
|
||||
// create an arch linux host (platform_like is empty)
|
||||
hostArch := createOrbitEnrolledHost(t, "arch", "host1", s.ds)
|
||||
createDeviceTokenForHost(t, s.ds, hostArch.ID, uuid.NewString())
|
||||
|
||||
// create a ubuntu host (platform_like is "debian")
|
||||
hostUbuntu := createOrbitEnrolledHost(t, "ubuntu", "host2", s.ds)
|
||||
hostUbuntu.PlatformLike = "debian"
|
||||
require.NoError(t, s.ds.UpdateHost(ctx, hostUbuntu))
|
||||
createDeviceTokenForHost(t, s.ds, hostUbuntu.ID, uuid.NewString())
|
||||
|
||||
// trigger setup experience for arch
|
||||
var orbitInitResponse orbitSetupExperienceInitResponse
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/init", orbitSetupExperienceInitRequest{
|
||||
OrbitNodeKey: *hostArch.OrbitNodeKey,
|
||||
}, http.StatusOK, &orbitInitResponse)
|
||||
require.True(t, orbitInitResponse.Result.Enabled)
|
||||
|
||||
// get status of the "Setup experience", only the .sh is compatible
|
||||
var orbitStatusResponse getOrbitSetupExperienceStatusResponse
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", getOrbitSetupExperienceStatusRequest{
|
||||
OrbitNodeKey: *hostArch.OrbitNodeKey,
|
||||
}, http.StatusOK, &orbitStatusResponse)
|
||||
require.Nil(t, orbitStatusResponse.Results.Script)
|
||||
require.Nil(t, orbitStatusResponse.Results.BootstrapPackage)
|
||||
require.Len(t, orbitStatusResponse.Results.ConfigurationProfiles, 0)
|
||||
require.Nil(t, orbitStatusResponse.Results.AccountConfiguration)
|
||||
require.False(t, orbitStatusResponse.Results.RequireAllSoftware)
|
||||
|
||||
require.Len(t, orbitStatusResponse.Results.Software, 1)
|
||||
require.Equal(t, payloadSh.Title, orbitStatusResponse.Results.Software[0].Name)
|
||||
require.NotNil(t, orbitStatusResponse.Results.Software[0].SoftwareTitleID)
|
||||
require.Equal(t, shTitleID, *orbitStatusResponse.Results.Software[0].SoftwareTitleID)
|
||||
|
||||
// trigger setup experience for ubuntu
|
||||
orbitInitResponse = orbitSetupExperienceInitResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/init", orbitSetupExperienceInitRequest{
|
||||
OrbitNodeKey: *hostUbuntu.OrbitNodeKey,
|
||||
}, http.StatusOK, &orbitInitResponse)
|
||||
require.True(t, orbitInitResponse.Result.Enabled)
|
||||
|
||||
// get status of the "Setup experience", both the .sh and .deb are enqueued
|
||||
orbitStatusResponse = getOrbitSetupExperienceStatusResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", getOrbitSetupExperienceStatusRequest{
|
||||
OrbitNodeKey: *hostUbuntu.OrbitNodeKey,
|
||||
}, http.StatusOK, &orbitStatusResponse)
|
||||
require.Nil(t, orbitStatusResponse.Results.Script)
|
||||
require.Nil(t, orbitStatusResponse.Results.BootstrapPackage)
|
||||
require.Len(t, orbitStatusResponse.Results.ConfigurationProfiles, 0)
|
||||
require.Nil(t, orbitStatusResponse.Results.AccountConfiguration)
|
||||
require.False(t, orbitStatusResponse.Results.RequireAllSoftware)
|
||||
|
||||
require.Len(t, orbitStatusResponse.Results.Software, 2)
|
||||
sort.Slice(orbitStatusResponse.Results.Software, func(i, j int) bool {
|
||||
return orbitStatusResponse.Results.Software[i].Name < orbitStatusResponse.Results.Software[j].Name
|
||||
})
|
||||
require.Equal(t, payloadRuby.Title, orbitStatusResponse.Results.Software[0].Name)
|
||||
require.NotNil(t, orbitStatusResponse.Results.Software[0].SoftwareTitleID)
|
||||
require.Equal(t, debTitleID, *orbitStatusResponse.Results.Software[0].SoftwareTitleID)
|
||||
require.Equal(t, payloadSh.Title, orbitStatusResponse.Results.Software[1].Name)
|
||||
require.NotNil(t, orbitStatusResponse.Results.Software[1].SoftwareTitleID)
|
||||
require.Equal(t, shTitleID, *orbitStatusResponse.Results.Software[1].SoftwareTitleID)
|
||||
}
|
||||
|
|
|
|||
3
server/service/testdata/software-installers/script.sh
vendored
Normal file
3
server/service/testdata/software-installers/script.sh
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "script"
|
||||
|
|
@ -158,7 +158,7 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = mds.EnqueueSetupExperienceItems(ctx, "darwin", *hostUUID, teamID)
|
||||
_, err = mds.EnqueueSetupExperienceItems(ctx, "darwin", "darwin", *hostUUID, teamID)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to enqueue setup experience items for host %s: %v", *hostUUID, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue