Bugfix: properly enqueue compatible setup experience items for arch/omarchy linux (#41778)

This commit is contained in:
Martin Angers 2026-03-17 15:04:33 -04:00 committed by GitHub
parent 5a266bfaca
commit 8c85ef8ad3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 189 additions and 48 deletions

View file

@ -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.

View file

@ -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")
}

View file

@ -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)

View file

@ -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")
}

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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 {

View file

@ -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")
}

View file

@ -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)

View file

@ -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)
}

View file

@ -0,0 +1,3 @@
#!/bin/bash
echo "script"

View file

@ -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)
}