Clean up setup experience cancellation behavior (#43437)
Fixes #34288. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [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/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests - [ ] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Setup experience cancellations now create explicit cancellation activities for skipped/failed software and VPP app installs, plus a new "Canceled setup experience" activity type and a from_setup_experience flag. Activity text and host activity views now indicate "during setup experience" when applicable. * **Tests** * Added and updated tests for cancellation activity creation, VPP license-failure handling, and WasFromAutomation/from_setup_experience behaviors. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -319,10 +319,10 @@ func (svc *Service) getHostSetupExperienceStatus(ctx context.Context, host *flee
|
||||
return nil, ctxerr.Wrap(ctx, err, "listing setup experience results")
|
||||
}
|
||||
|
||||
// Mark canceled items as failed.
|
||||
err = svc.failCancelledSetupExperienceInstalls(ctx, host.ID, hostUUID, host.DisplayName(), results)
|
||||
// Add activities for canceled installs + setup experience run
|
||||
err = svc.recordCanceledSetupExperienceSoftwareActivities(ctx, host.ID, hostUUID, host.DisplayName(), results)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "failing cancelled setup experience installs")
|
||||
return nil, ctxerr.Wrap(ctx, err, "recording cancelled setup experience installs")
|
||||
}
|
||||
|
||||
var software []*fleet.SetupExperienceStatusResult
|
||||
|
||||
+19
-37
@@ -169,9 +169,8 @@ func (svc *Service) GetOrbitSetupExperienceStatus(ctx context.Context, orbitNode
|
||||
}
|
||||
}
|
||||
|
||||
err = svc.failCancelledSetupExperienceInstalls(ctx, host.ID, host.UUID, host.DisplayName(), res)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "failing cancelled setup experience installs")
|
||||
if err = svc.recordCanceledSetupExperienceSoftwareActivities(ctx, host.ID, host.UUID, host.DisplayName(), res); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "recording cancelled setup experience installs")
|
||||
}
|
||||
|
||||
payload := &fleet.SetupExperienceStatusPayload{
|
||||
@@ -229,7 +228,7 @@ func (svc *Service) GetOrbitSetupExperienceStatus(ctx context.Context, orbitNode
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func (svc *Service) failCancelledSetupExperienceInstalls(
|
||||
func (svc *Service) recordCanceledSetupExperienceSoftwareActivities(
|
||||
ctx context.Context,
|
||||
hostID uint,
|
||||
hostUUID string,
|
||||
@@ -241,51 +240,34 @@ func (svc *Service) failCancelledSetupExperienceInstalls(
|
||||
continue
|
||||
}
|
||||
r.Status = fleet.SetupExperienceStatusFailure
|
||||
svc.logger.InfoContext(ctx, "marking setup experience software as failed due to cancellation", "host_uuid", hostUUID, "software_name", r.Name)
|
||||
svc.logger.InfoContext(ctx, "emitting activity for canceled setup experience software", "host_uuid", hostUUID, "software_name", r.Name)
|
||||
err := svc.ds.UpdateSetupExperienceStatusResult(ctx, r)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "failing cancelled setup experience software install")
|
||||
return ctxerr.Wrap(ctx, err, "marking canceled setup experience software install as failed")
|
||||
}
|
||||
// TODO -- support recording activity for failed VPP apps as well.
|
||||
// https://github.com/fleetdm/fleet/issues/34288
|
||||
if r.IsForSoftwarePackage() {
|
||||
softwarePackage := ""
|
||||
var source *string
|
||||
installerMeta, err := svc.ds.GetSoftwareInstallerMetadataByID(ctx, *r.SoftwareInstallerID)
|
||||
if err != nil && !fleet.IsNotFound(err) {
|
||||
return ctxerr.Wrap(ctx, err, "getting software installer metadata for cancelled setup experience software install")
|
||||
}
|
||||
if installerMeta != nil {
|
||||
softwarePackage = installerMeta.Name
|
||||
// Get the software title to retrieve the source
|
||||
if installerMeta.TitleID != nil {
|
||||
title, err := svc.ds.SoftwareTitleByID(ctx, *installerMeta.TitleID, nil, fleet.TeamFilter{})
|
||||
if err != nil && !fleet.IsNotFound(err) {
|
||||
return ctxerr.Wrap(ctx, err, "getting software title for cancelled setup experience software install")
|
||||
}
|
||||
if title != nil {
|
||||
source = &title.Source
|
||||
}
|
||||
}
|
||||
}
|
||||
activity := fleet.ActivityTypeInstalledSoftware{
|
||||
if err := svc.NewActivity(ctx, nil, fleet.ActivityTypeCanceledInstallSoftware{
|
||||
HostID: hostID,
|
||||
HostDisplayName: hostDisplayName,
|
||||
SoftwareTitle: r.Name,
|
||||
SoftwarePackage: softwarePackage,
|
||||
InstallUUID: ptr.ValOrZero(r.HostSoftwareInstallsExecutionID),
|
||||
Status: "failed",
|
||||
SelfService: false,
|
||||
Source: source,
|
||||
SoftwareTitleID: ptr.ValOrZero(r.SoftwareTitleID),
|
||||
FromSetupExperience: true,
|
||||
}); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "creating activity for canceled setup experience software install")
|
||||
}
|
||||
err = svc.NewActivity(ctx, nil, activity)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "creating activity for cancelled setup experience software install")
|
||||
} else if r.IsForVPPApp() {
|
||||
if err := svc.NewActivity(ctx, nil, fleet.ActivityTypeCanceledInstallAppStoreApp{
|
||||
HostID: hostID,
|
||||
HostDisplayName: hostDisplayName,
|
||||
SoftwareTitle: r.Name,
|
||||
SoftwareTitleID: ptr.ValOrZero(r.SoftwareTitleID),
|
||||
FromSetupExperience: true,
|
||||
}); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "creating activity for canceled setup experience VPP app install")
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/mock"
|
||||
svcmock "github.com/fleetdm/fleet/v4/server/mock/service"
|
||||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRecordCanceledSetupExperienceSoftwareActivities(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ds := new(mock.Store)
|
||||
baseSvc := new(svcmock.Service)
|
||||
|
||||
svc := &Service{
|
||||
Service: baseSvc,
|
||||
ds: ds,
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
hostID := uint(42)
|
||||
hostUUID := "host-uuid-1"
|
||||
hostDisplayName := "Test Host"
|
||||
|
||||
t.Run("skips non-cancelled results", func(t *testing.T) {
|
||||
var activityCreated bool
|
||||
baseSvc.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
activityCreated = true
|
||||
return nil
|
||||
}
|
||||
ds.UpdateSetupExperienceStatusResultFunc = func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
results := []*fleet.SetupExperienceStatusResult{
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Status: fleet.SetupExperienceStatusPending,
|
||||
SoftwareInstallerID: ptr.Uint(1),
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Status: fleet.SetupExperienceStatusSuccess,
|
||||
VPPAppTeamID: ptr.Uint(2),
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Status: fleet.SetupExperienceStatusRunning,
|
||||
VPPAppTeamID: ptr.Uint(3),
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Status: fleet.SetupExperienceStatusFailure,
|
||||
SetupExperienceScriptID: ptr.Uint(4),
|
||||
},
|
||||
}
|
||||
|
||||
err := svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, results)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, activityCreated, "no activity should be created for non-cancelled results")
|
||||
assert.False(t, ds.UpdateSetupExperienceStatusResultFuncInvoked, "no update should be called for non-cancelled results")
|
||||
})
|
||||
|
||||
t.Run("software package cancelled emits canceled_install_software activity with FromSetupExperience", func(t *testing.T) {
|
||||
ds.UpdateSetupExperienceStatusResultFuncInvoked = false
|
||||
|
||||
installerID := uint(10)
|
||||
titleID := uint(100)
|
||||
|
||||
ds.UpdateSetupExperienceStatusResultFunc = func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var createdActivities []fleet.ActivityDetails
|
||||
var createdUser *fleet.User
|
||||
baseSvc.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
createdUser = user
|
||||
createdActivities = append(createdActivities, activity)
|
||||
return nil
|
||||
}
|
||||
|
||||
// The failed item that caused the cancellation
|
||||
failedTitleID := uint(999)
|
||||
results := []*fleet.SetupExperienceStatusResult{
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "FailedApp",
|
||||
Status: fleet.SetupExperienceStatusFailure,
|
||||
SoftwareInstallerID: ptr.Uint(99),
|
||||
SoftwareTitleID: &failedTitleID,
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "DummyApp",
|
||||
Status: fleet.SetupExperienceStatusCancelled,
|
||||
SoftwareInstallerID: &installerID,
|
||||
SoftwareTitleID: &titleID,
|
||||
HostSoftwareInstallsExecutionID: ptr.String("exec-uuid-1"),
|
||||
},
|
||||
}
|
||||
|
||||
err := svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, results)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Status should have been changed to failure
|
||||
assert.Equal(t, fleet.SetupExperienceStatusFailure, results[1].Status)
|
||||
|
||||
// Update should have been called
|
||||
assert.True(t, ds.UpdateSetupExperienceStatusResultFuncInvoked)
|
||||
|
||||
// Should have 1 activity: canceled install (canceled_setup_experience is emitted earlier)
|
||||
require.Len(t, createdActivities, 1)
|
||||
|
||||
// Canceled install software
|
||||
canceledAct, ok := createdActivities[0].(fleet.ActivityTypeCanceledInstallSoftware)
|
||||
require.True(t, ok, "expected ActivityTypeCanceledInstallSoftware, got %T", createdActivities[0])
|
||||
assert.Equal(t, hostID, canceledAct.HostID)
|
||||
assert.Equal(t, hostDisplayName, canceledAct.HostDisplayName)
|
||||
assert.Equal(t, "DummyApp", canceledAct.SoftwareTitle)
|
||||
assert.Equal(t, titleID, canceledAct.SoftwareTitleID)
|
||||
assert.True(t, canceledAct.FromSetupExperience, "FromSetupExperience should be true")
|
||||
assert.True(t, canceledAct.WasFromAutomation(), "WasFromAutomation should be true")
|
||||
|
||||
// Should be created with nil user (Fleet-initiated)
|
||||
assert.Nil(t, createdUser)
|
||||
})
|
||||
|
||||
t.Run("VPP app cancelled emits canceled_install_app_store_app activity with FromSetupExperience", func(t *testing.T) {
|
||||
ds.UpdateSetupExperienceStatusResultFuncInvoked = false
|
||||
|
||||
vppTeamID := uint(20)
|
||||
adamID := "12345"
|
||||
softwareTitleID := uint(200)
|
||||
|
||||
ds.UpdateSetupExperienceStatusResultFunc = func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var createdActivities []fleet.ActivityDetails
|
||||
baseSvc.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
createdActivities = append(createdActivities, activity)
|
||||
return nil
|
||||
}
|
||||
|
||||
failedTitleID := uint(888)
|
||||
results := []*fleet.SetupExperienceStatusResult{
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "FailedVPP",
|
||||
Status: fleet.SetupExperienceStatusFailure,
|
||||
VPPAppTeamID: ptr.Uint(99),
|
||||
SoftwareTitleID: &failedTitleID,
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "VPPApp",
|
||||
Status: fleet.SetupExperienceStatusCancelled,
|
||||
VPPAppTeamID: &vppTeamID,
|
||||
VPPAppAdamID: &adamID,
|
||||
SoftwareTitleID: &softwareTitleID,
|
||||
},
|
||||
}
|
||||
|
||||
err := svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, results)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Status should have been changed to failure
|
||||
assert.Equal(t, fleet.SetupExperienceStatusFailure, results[1].Status)
|
||||
|
||||
// Should have 1 activity: canceled VPP install (canceled_setup_experience is emitted earlier)
|
||||
require.Len(t, createdActivities, 1)
|
||||
|
||||
// Canceled install app store app
|
||||
canceledAct, ok := createdActivities[0].(fleet.ActivityTypeCanceledInstallAppStoreApp)
|
||||
require.True(t, ok, "expected ActivityTypeCanceledInstallAppStoreApp, got %T", createdActivities[0])
|
||||
assert.Equal(t, hostID, canceledAct.HostID)
|
||||
assert.Equal(t, hostDisplayName, canceledAct.HostDisplayName)
|
||||
assert.Equal(t, "VPPApp", canceledAct.SoftwareTitle)
|
||||
assert.Equal(t, softwareTitleID, canceledAct.SoftwareTitleID)
|
||||
assert.True(t, canceledAct.FromSetupExperience)
|
||||
assert.True(t, canceledAct.WasFromAutomation())
|
||||
})
|
||||
|
||||
t.Run("mixed cancelled and non-cancelled results", func(t *testing.T) {
|
||||
ds.UpdateSetupExperienceStatusResultFuncInvoked = false
|
||||
|
||||
installerID := uint(30)
|
||||
titleID := uint(300)
|
||||
vppTeamID := uint(40)
|
||||
adamID := "67890"
|
||||
vppTitleID := uint(400)
|
||||
|
||||
ds.UpdateSetupExperienceStatusResultFunc = func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var activities []fleet.ActivityDetails
|
||||
baseSvc.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
activities = append(activities, activity)
|
||||
return nil
|
||||
}
|
||||
|
||||
failedTitleID := uint(777)
|
||||
results := []*fleet.SetupExperienceStatusResult{
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "FailedApp",
|
||||
Status: fleet.SetupExperienceStatusFailure,
|
||||
SoftwareInstallerID: ptr.Uint(50),
|
||||
SoftwareTitleID: &failedTitleID,
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "SuccessApp",
|
||||
Status: fleet.SetupExperienceStatusSuccess,
|
||||
SoftwareInstallerID: ptr.Uint(51),
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "CancelledSW",
|
||||
Status: fleet.SetupExperienceStatusCancelled,
|
||||
SoftwareInstallerID: &installerID,
|
||||
SoftwareTitleID: &titleID,
|
||||
HostSoftwareInstallsExecutionID: ptr.String("exec-uuid-3"),
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "PendingVPP",
|
||||
Status: fleet.SetupExperienceStatusPending,
|
||||
VPPAppTeamID: ptr.Uint(60),
|
||||
},
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "CancelledVPP",
|
||||
Status: fleet.SetupExperienceStatusCancelled,
|
||||
VPPAppTeamID: &vppTeamID,
|
||||
VPPAppAdamID: &adamID,
|
||||
SoftwareTitleID: &vppTitleID,
|
||||
},
|
||||
}
|
||||
|
||||
err := svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, results)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Only the two cancelled results should have their status changed
|
||||
assert.Equal(t, fleet.SetupExperienceStatusFailure, results[0].Status) // was already failed
|
||||
assert.Equal(t, fleet.SetupExperienceStatusSuccess, results[1].Status) // unchanged
|
||||
assert.Equal(t, fleet.SetupExperienceStatusFailure, results[2].Status) // cancelled -> failed
|
||||
assert.Equal(t, fleet.SetupExperienceStatusPending, results[3].Status) // unchanged
|
||||
assert.Equal(t, fleet.SetupExperienceStatusFailure, results[4].Status) // cancelled -> failed
|
||||
|
||||
// Two activities: canceled sw install + canceled vpp install (canceled_setup_experience emitted earlier)
|
||||
require.Len(t, activities, 2)
|
||||
|
||||
swAct, ok := activities[0].(fleet.ActivityTypeCanceledInstallSoftware)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "CancelledSW", swAct.SoftwareTitle)
|
||||
assert.True(t, swAct.FromSetupExperience)
|
||||
|
||||
vppAct, ok := activities[1].(fleet.ActivityTypeCanceledInstallAppStoreApp)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "CancelledVPP", vppAct.SoftwareTitle)
|
||||
assert.True(t, vppAct.FromSetupExperience)
|
||||
})
|
||||
|
||||
t.Run("script cancellation does not trigger activity", func(t *testing.T) {
|
||||
ds.UpdateSetupExperienceStatusResultFuncInvoked = false
|
||||
|
||||
ds.UpdateSetupExperienceStatusResultFunc = func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var activityCreated bool
|
||||
baseSvc.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
activityCreated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptID := uint(70)
|
||||
results := []*fleet.SetupExperienceStatusResult{
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "setup.sh",
|
||||
Status: fleet.SetupExperienceStatusCancelled,
|
||||
SetupExperienceScriptID: &scriptID,
|
||||
},
|
||||
}
|
||||
|
||||
err := svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, results)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Status should still be changed to failure
|
||||
assert.Equal(t, fleet.SetupExperienceStatusFailure, results[0].Status)
|
||||
// But no activity should be created for script cancellations
|
||||
assert.False(t, activityCreated)
|
||||
})
|
||||
|
||||
t.Run("empty results returns nil", func(t *testing.T) {
|
||||
err := svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, []*fleet.SetupExperienceStatusResult{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("cancelled items without failed item still emit individual cancel activities", func(t *testing.T) {
|
||||
ds.UpdateSetupExperienceStatusResultFuncInvoked = false
|
||||
|
||||
installerID := uint(10)
|
||||
titleID := uint(100)
|
||||
|
||||
ds.UpdateSetupExperienceStatusResultFunc = func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var createdActivities []fleet.ActivityDetails
|
||||
baseSvc.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
createdActivities = append(createdActivities, activity)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only cancelled items, no failed item that triggered them
|
||||
results := []*fleet.SetupExperienceStatusResult{
|
||||
{
|
||||
HostUUID: hostUUID,
|
||||
Name: "DummyApp",
|
||||
Status: fleet.SetupExperienceStatusCancelled,
|
||||
SoftwareInstallerID: &installerID,
|
||||
SoftwareTitleID: &titleID,
|
||||
HostSoftwareInstallsExecutionID: ptr.String("exec-uuid-1"),
|
||||
},
|
||||
}
|
||||
|
||||
err := svc.recordCanceledSetupExperienceSoftwareActivities(ctx, hostID, hostUUID, hostDisplayName, results)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should only have the canceled install activity
|
||||
require.Len(t, createdActivities, 1)
|
||||
_, ok := createdActivities[0].(fleet.ActivityTypeCanceledInstallSoftware)
|
||||
require.True(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCanceledActivityWasFromAutomation(t *testing.T) {
|
||||
t.Run("CanceledInstallSoftware", func(t *testing.T) {
|
||||
act := fleet.ActivityTypeCanceledInstallSoftware{
|
||||
HostID: 1,
|
||||
HostDisplayName: "host",
|
||||
SoftwareTitle: "title",
|
||||
SoftwareTitleID: 1,
|
||||
FromSetupExperience: false,
|
||||
}
|
||||
assert.False(t, act.WasFromAutomation())
|
||||
|
||||
act.FromSetupExperience = true
|
||||
assert.True(t, act.WasFromAutomation())
|
||||
})
|
||||
|
||||
t.Run("CanceledInstallAppStoreApp", func(t *testing.T) {
|
||||
act := fleet.ActivityTypeCanceledInstallAppStoreApp{
|
||||
HostID: 1,
|
||||
HostDisplayName: "host",
|
||||
SoftwareTitle: "title",
|
||||
SoftwareTitleID: 1,
|
||||
FromSetupExperience: false,
|
||||
}
|
||||
assert.False(t, act.WasFromAutomation())
|
||||
|
||||
act.FromSetupExperience = true
|
||||
assert.True(t, act.WasFromAutomation())
|
||||
})
|
||||
}
|
||||
@@ -276,6 +276,24 @@ func (svc *Service) SetupExperienceNextStep(ctx context.Context, host *fleet.Hos
|
||||
svc.logger.WarnContext(ctx, "got an error when attempting to enqueue VPP app install", "err", err, "adam_id", sw.VPPAppAdamID)
|
||||
sw.Status = fleet.SetupExperienceStatusFailure
|
||||
sw.Error = ptr.String(err.Error())
|
||||
// Persist the failure before cancelling other steps, so that
|
||||
// maybeCancelPendingSetupExperienceSteps can find the failed
|
||||
// item from its loaded statuses.
|
||||
if err := svc.ds.UpdateSetupExperienceStatusResult(ctx, sw); err != nil {
|
||||
return false, ctxerr.Wrap(ctx, err, "updating setup experience with vpp install failure")
|
||||
}
|
||||
failActivity := fleet.ActivityInstalledAppStoreApp{
|
||||
HostID: host.ID,
|
||||
HostDisplayName: host.DisplayName(),
|
||||
SoftwareTitle: sw.Name,
|
||||
AppStoreID: ptr.ValOrZero(sw.VPPAppAdamID),
|
||||
Status: string(fleet.SoftwareInstallFailed),
|
||||
HostPlatform: host.Platform,
|
||||
FromSetupExperience: true,
|
||||
}
|
||||
if actErr := svc.NewActivity(ctx, nil, failActivity); actErr != nil {
|
||||
svc.logger.WarnContext(ctx, "failed to create activity for VPP app install failure during setup experience", "err", actErr)
|
||||
}
|
||||
// At this point we need to check whether the "cancel if software install fails" setting is active,
|
||||
// in which case we'll cancel the remaining pending items.
|
||||
requireAllSoftware, err := svc.IsAllSetupExperienceSoftwareRequired(ctx, host)
|
||||
@@ -291,9 +309,9 @@ func (svc *Service) SetupExperienceNextStep(ctx context.Context, host *fleet.Hos
|
||||
} else {
|
||||
sw.NanoCommandUUID = &cmdUUID
|
||||
sw.Status = fleet.SetupExperienceStatusRunning
|
||||
}
|
||||
if err := svc.ds.UpdateSetupExperienceStatusResult(ctx, sw); err != nil {
|
||||
return false, ctxerr.Wrap(ctx, err, "updating setup experience with vpp install command uuid")
|
||||
if err := svc.ds.UpdateSetupExperienceStatusResult(ctx, sw); err != nil {
|
||||
return false, ctxerr.Wrap(ctx, err, "updating setup experience with vpp install command uuid")
|
||||
}
|
||||
}
|
||||
}
|
||||
case softwareRunning == 0 && len(scriptsPending) > 0:
|
||||
|
||||
Reference in New Issue
Block a user