fix: show script name in activity for setup experience script (#23944)

> Related issue: #23787 

This adds the script name to both the upcoming and past activities.

Demo video: https://www.youtube.com/watch?v=kLSsUZhyMC4

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [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] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Jahziel Villasana-Espinoza
2024-11-19 17:38:09 -05:00
committed by GitHub
parent 79086c1f17
commit 8a8b8403b2
12 changed files with 256 additions and 36 deletions
+2
View File
@@ -0,0 +1,2 @@
- Fixes a bug where the name of the setup experience script was not showing up in the activity for
that script execution.
+4 -3
View File
@@ -220,9 +220,10 @@ func (svc *Service) SetupExperienceNextStep(ctx context.Context, hostUUID string
return false, ctxerr.Errorf(ctx, "setup experience script missing content id: %d", *script.SetupExperienceScriptID)
}
req := &fleet.HostScriptRequestPayload{
HostID: host.ID,
ScriptName: script.Name,
ScriptContentID: *script.ScriptContentID,
HostID: host.ID,
ScriptName: script.Name,
ScriptContentID: *script.ScriptContentID,
SetupExperienceScriptID: script.SetupExperienceScriptID,
}
res, err := svc.ds.NewHostScriptExecutionRequest(ctx, req)
if err != nil {
+3 -1
View File
@@ -312,7 +312,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
JSON_OBJECT(
'host_id', hsr.host_id,
'host_display_name', COALESCE(hdn.display_name, ''),
'script_name', COALESCE(scr.name, ''),
'script_name', COALESCE(ses.name, COALESCE(scr.name, '')),
'script_execution_id', hsr.execution_id,
'async', NOT hsr.sync_request,
'policy_id', hsr.policy_id,
@@ -330,6 +330,8 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
scripts scr ON scr.id = hsr.script_id
LEFT OUTER JOIN
host_software_installs hsi ON hsi.execution_id = hsr.execution_id
LEFT OUTER JOIN
setup_experience_scripts ses ON ses.id = hsr.setup_experience_script_id
WHERE
hsr.host_id = :host_id AND
hsr.exit_code IS NULL AND
+19 -7
View File
@@ -541,6 +541,15 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) {
h2SelfService, err := ds.InsertSoftwareInstallRequest(noUserCtx, h2.ID, sw1Meta.InstallerID, true, nil)
require.NoError(t, err)
setupExpScript := &fleet.Script{Name: "setup_experience_script", ScriptContents: "setup_experience"}
err = ds.SetSetupExperienceScript(ctx, setupExpScript)
require.NoError(t, err)
ses, err := ds.GetSetupExperienceScript(ctx, h2.TeamID)
require.NoError(t, err)
hsr, err = ds.NewHostScriptExecutionRequest(ctx, &fleet.HostScriptRequestPayload{HostID: h2.ID, ScriptContents: "setup_experience", SetupExperienceScriptID: &ses.ID})
require.NoError(t, err)
h2SetupExp := hsr.ExecutionID
// create pending install and uninstall requests for h3 that will be deleted
_, err = ds.InsertSoftwareInstallRequest(ctx, h3.ID, sw3Meta.InstallerID, false, nil)
require.NoError(t, err)
@@ -560,7 +569,8 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) {
endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h2SelfService)
endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h2Bar)
endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_script_results", "execution_id", h2A, h2F)
SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_vpp_software_installs", "command_uuid", vppCommand1, vppCommand2)
endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_vpp_software_installs", "command_uuid", vppCommand1, vppCommand2)
SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_script_results", "execution_id", h2SetupExp)
execIDsWithUser := map[string]bool{
h1A: true,
@@ -576,11 +586,13 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) {
h2Bar: true,
vppCommand1: true,
vppCommand2: false,
h2SetupExp: false,
}
execIDsScriptName := map[string]string{
h1A: scr1.Name,
h1B: scr2.Name,
h2A: scr1.Name,
h1A: scr1.Name,
h1B: scr2.Name,
h2A: scr1.Name,
h2SetupExp: setupExpScript.Name,
}
execIDsSoftwareTitle := map[string]string{
h1Fleet: "foo",
@@ -641,10 +653,10 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) {
wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: true, TotalResults: 8},
},
{
opts: fleet.ListOptions{PerPage: 4},
opts: fleet.ListOptions{PerPage: 5},
hostID: h2.ID,
wantExecs: []string{h2SelfService, h2Bar, h2A, vppCommand2},
wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: false, TotalResults: 4},
wantExecs: []string{h2SelfService, h2Bar, h2A, vppCommand2, h2SetupExp},
wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: false, TotalResults: 5},
},
{
opts: fleet.ListOptions{},
+5 -3
View File
@@ -39,8 +39,8 @@ func (ds *Datastore) NewHostScriptExecutionRequest(ctx context.Context, request
func newHostScriptExecutionRequest(ctx context.Context, tx sqlx.ExtContext, request *fleet.HostScriptRequestPayload) (*fleet.HostScriptResult, error) {
const (
insStmt = `INSERT INTO host_script_results (host_id, execution_id, script_content_id, output, script_id, policy_id, user_id, sync_request) VALUES (?, ?, ?, '', ?, ?, ?, ?)`
getStmt = `SELECT hsr.id, hsr.host_id, hsr.execution_id, hsr.created_at, hsr.script_id, hsr.policy_id, hsr.user_id, hsr.sync_request, sc.contents as script_contents FROM host_script_results hsr JOIN script_contents sc WHERE sc.id = hsr.script_content_id AND hsr.id = ?`
insStmt = `INSERT INTO host_script_results (host_id, execution_id, script_content_id, output, script_id, policy_id, user_id, sync_request, setup_experience_script_id) VALUES (?, ?, ?, '', ?, ?, ?, ?, ?)`
getStmt = `SELECT hsr.id, hsr.host_id, hsr.execution_id, hsr.created_at, hsr.script_id, hsr.policy_id, hsr.user_id, hsr.sync_request, sc.contents as script_contents, hsr.setup_experience_script_id FROM host_script_results hsr JOIN script_contents sc WHERE sc.id = hsr.script_content_id AND hsr.id = ?`
)
execID := uuid.New().String()
@@ -52,6 +52,7 @@ func newHostScriptExecutionRequest(ctx context.Context, tx sqlx.ExtContext, requ
request.PolicyID,
request.UserID,
request.SyncRequest,
request.SetupExperienceScriptID,
)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "new host script execution request")
@@ -269,7 +270,8 @@ func (ds *Datastore) getHostScriptExecutionResultDB(ctx context.Context, q sqlx.
hsr.created_at,
hsr.user_id,
hsr.sync_request,
hsr.host_deleted_at
hsr.host_deleted_at,
hsr.setup_experience_script_id
FROM
host_script_results hsr
JOIN
@@ -404,6 +404,32 @@ WHERE
return &script, nil
}
func (ds *Datastore) GetSetupExperienceScriptByID(ctx context.Context, scriptID uint) (*fleet.Script, error) {
query := `
SELECT
id,
team_id,
name,
script_content_id,
created_at,
updated_at
FROM
setup_experience_scripts
WHERE
id = ?
`
var script fleet.Script
if err := sqlx.GetContext(ctx, ds.reader(ctx), &script, query, scriptID); err != nil {
if err == sql.ErrNoRows {
return nil, ctxerr.Wrap(ctx, notFound("SetupExperienceScript"), "get setup experience script by id")
}
return nil, ctxerr.Wrap(ctx, err, "get setup experience script by id")
}
return &script, nil
}
func (ds *Datastore) SetSetupExperienceScript(ctx context.Context, script *fleet.Script) error {
err := ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
var err error
@@ -29,6 +29,7 @@ func TestSetupExperience(t *testing.T) {
{"ListSetupExperienceStatusResults", testSetupExperienceStatusResults},
{"SetupExperienceScriptCRUD", testSetupExperienceScriptCRUD},
{"TestHostInSetupExperience", testHostInSetupExperience},
{"TestGetSetupExperienceScriptByID", testGetSetupExperienceScriptByID},
}
for _, c := range cases {
@@ -675,7 +676,6 @@ func testSetSetupExperienceTitles(t *testing.T, ds *Datastore) {
assert.False(t, *titles[0].SoftwarePackage.InstallDuringSetup)
assert.False(t, *titles[1].SoftwarePackage.InstallDuringSetup)
assert.False(t, *titles[2].AppStoreApp.InstallDuringSetup)
}
func testSetupExperienceStatusResults(t *testing.T, ds *Datastore) {
@@ -909,3 +909,28 @@ func testHostInSetupExperience(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.False(t, inSetupExperience)
}
func testGetSetupExperienceScriptByID(t *testing.T, ds *Datastore) {
ctx := context.Background()
script := &fleet.Script{
Name: "setup_experience_script",
ScriptContents: "echo hello",
}
err := ds.SetSetupExperienceScript(ctx, script)
require.NoError(t, err)
scriptByTeamID, err := ds.GetSetupExperienceScript(ctx, nil)
require.NoError(t, err)
gotScript, err := ds.GetSetupExperienceScriptByID(ctx, scriptByTeamID.ID)
require.NoError(t, err)
require.Equal(t, script.Name, gotScript.Name)
require.NotZero(t, gotScript.ScriptContentID)
b, err := ds.GetAnyScriptContents(ctx, gotScript.ScriptContentID)
require.NoError(t, err)
require.Equal(t, script.ScriptContents, string(b))
}
+32
View File
@@ -1795,14 +1795,46 @@ type Datastore interface {
// Setup Experience
//
// ListSetupExperienceResultsByHostUUID lists the setup experience results for a host by its UUID.
ListSetupExperienceResultsByHostUUID(ctx context.Context, hostUUID string) ([]*SetupExperienceStatusResult, error)
// UpdateSetupExperienceStatusResult updates the given setup experience status result.
UpdateSetupExperienceStatusResult(ctx context.Context, status *SetupExperienceStatusResult) error
// EnqueueSetupExperienceItems enqueues the relevant setup experience items (software and
// script) for a given host. It first clears out any pre-existing setup experience items that
// were previously enqueued for the host (since the setup experience only happens once during
// the initial device setup). It then adds any software and script that have been configured for
// this team to the host's queue and sets their status to pending. If any items were enqueued,
// it returns true, otherwise it returns false.
EnqueueSetupExperienceItems(ctx context.Context, hostUUID string, teamID uint) (bool, error)
// GetSetupExperienceScript gets the setup experience script for a team. There can only be 1
// setup experience script per team.
GetSetupExperienceScript(ctx context.Context, teamID *uint) (*Script, error)
// GetSetupExperienceScriptByID gets the setup experience script by its ID.
GetSetupExperienceScriptByID(ctx context.Context, scriptID uint) (*Script, error)
// SetSetupExperienceScript sets the setup experience script to the given script.
SetSetupExperienceScript(ctx context.Context, script *Script) error
// DeleteSetupExperienceScript deletes the setup experience script for the given team.
DeleteSetupExperienceScript(ctx context.Context, teamID *uint) error
// MaybeUpdateSetupExperienceScriptStatus updates the status of the setup experience script for
// the given host if the script result row exists. If there was an update, it returns true.
// Otherwise, it returns false.
MaybeUpdateSetupExperienceScriptStatus(ctx context.Context, hostUUID string, executionID string, status SetupExperienceStatusResultStatus) (bool, error)
// MaybeUpdateSetupExperienceSoftwareInstallStatus updates the status of the setup experience
// software installer for the given host if the software installer result row exists. If there
// was an update, it returns true. Otherwise, it returns false.
MaybeUpdateSetupExperienceSoftwareInstallStatus(ctx context.Context, hostUUID string, executionID string, status SetupExperienceStatusResultStatus) (bool, error)
// MaybeUpdateSetupExperienceVPPStatus updates the status of the setup experience
// VPP app for the given host if the VPP app installer row exists. If there was an update, it
// returns true. Otherwise, it returns false.
MaybeUpdateSetupExperienceVPPStatus(ctx context.Context, hostUUID string, commandUUID string, status SetupExperienceStatusResultStatus) (bool, error)
// Fleet-maintained apps
+7
View File
@@ -149,6 +149,9 @@ type HostScriptRequestPayload struct {
// SyncRequest is filled automatically based on the endpoint used to create
// the execution request (synchronous or asynchronous).
SyncRequest bool `json:"-"`
// SetupExperienceScriptID is the ID of the setup experience script related to this request
// payload, if such a script exists.
SetupExperienceScriptID *uint `json:"-"`
}
func (r HostScriptRequestPayload) ValidateParams(waitForResult time.Duration) error {
@@ -251,6 +254,10 @@ type HostScriptResult struct {
// results can still be returned to see activity details after the host got
// deleted.
HostDeletedAt *time.Time `json:"-" db:"host_deleted_at"`
// SetupExperienceScriptID is the ID of the setup experience script, if this script execution
// was part of setup experience.
SetupExperienceScriptID *uint `json:"-" db:"setup_experience_script_id"`
}
func (hsr HostScriptResult) AuthzType() string {
+12
View File
@@ -1141,6 +1141,8 @@ type EnqueueSetupExperienceItemsFunc func(ctx context.Context, hostUUID string,
type GetSetupExperienceScriptFunc func(ctx context.Context, teamID *uint) (*fleet.Script, error)
type GetSetupExperienceScriptByIDFunc func(ctx context.Context, scriptID uint) (*fleet.Script, error)
type SetSetupExperienceScriptFunc func(ctx context.Context, script *fleet.Script) error
type DeleteSetupExperienceScriptFunc func(ctx context.Context, teamID *uint) error
@@ -2844,6 +2846,9 @@ type DataStore struct {
GetSetupExperienceScriptFunc GetSetupExperienceScriptFunc
GetSetupExperienceScriptFuncInvoked bool
GetSetupExperienceScriptByIDFunc GetSetupExperienceScriptByIDFunc
GetSetupExperienceScriptByIDFuncInvoked bool
SetSetupExperienceScriptFunc SetSetupExperienceScriptFunc
SetSetupExperienceScriptFuncInvoked bool
@@ -6800,6 +6805,13 @@ func (s *DataStore) GetSetupExperienceScript(ctx context.Context, teamID *uint)
return s.GetSetupExperienceScriptFunc(ctx, teamID)
}
func (s *DataStore) GetSetupExperienceScriptByID(ctx context.Context, scriptID uint) (*fleet.Script, error) {
s.mu.Lock()
s.GetSetupExperienceScriptByIDFuncInvoked = true
s.mu.Unlock()
return s.GetSetupExperienceScriptByIDFunc(ctx, scriptID)
}
func (s *DataStore) SetSetupExperienceScript(ctx context.Context, script *fleet.Script) error {
s.mu.Lock()
s.SetSetupExperienceScriptFuncInvoked = true
+110 -20
View File
@@ -1952,6 +1952,7 @@ func (s *integrationMDMTestSuite) createTeamDeviceForSetupExperienceWithProfileS
require.Len(t, listHostsRes.Hosts, 1)
require.Equal(t, listHostsRes.Hosts[0].HardwareSerial, teamDevice.SerialNumber)
enrolledHost := listHostsRes.Hosts[0].Host
enrolledHost.TeamID = &tm.ID
// transfer it to the team
s.Do("POST", "/api/v1/fleet/hosts/transfer",
@@ -2076,6 +2077,57 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
// The /setup_experience/status endpoint doesn't return the various IDs for executions, so pull
// it out manually
results, err := s.ds.ListSetupExperienceResultsByHostUUID(ctx, enrolledHost.UUID)
require.Len(t, results, 2)
require.NoError(t, err)
var installUUID string
for _, r := range results {
if r.HostSoftwareInstallsExecutionID != nil {
installUUID = *r.HostSoftwareInstallsExecutionID
}
}
require.NotEmpty(t, installUUID)
// Need to get the software title to get the package name
var getSoftwareTitleResp getSoftwareTitleResponse
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/software/titles/%d", *statusResp.Results.Software[0].SoftwareTitleID), nil, http.StatusOK, &getSoftwareTitleResp, "team_id", fmt.Sprintf("%d", *enrolledHost.TeamID))
require.NotNil(t, getSoftwareTitleResp.SoftwareTitle)
require.NotNil(t, getSoftwareTitleResp.SoftwareTitle.SoftwarePackage)
debugPrintActivities := func(activities []*fleet.Activity) []string {
var res []string
for _, activity := range activities {
res = append(res, fmt.Sprintf("%+v", activity))
}
return res
}
// Check upcoming activities: we should only have the software upcoming because we don't run the
// script until after the software is done
var hostActivitiesResp listHostUpcomingActivitiesResponse
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities/upcoming", enrolledHost.ID),
nil, http.StatusOK, &hostActivitiesResp)
expectedActivityDetail := fmt.Sprintf(`
{
"status": "pending_install",
"host_id": %d,
"policy_id": null,
"policy_name": null,
"install_uuid": "%s",
"self_service": false,
"software_title": "%s",
"software_package": "%s",
"host_display_name": "%s"
}
`, enrolledHost.ID, installUUID, getSoftwareTitleResp.SoftwareTitle.Name, getSoftwareTitleResp.SoftwareTitle.SoftwarePackage.Name, enrolledHost.DisplayName())
require.Len(t, hostActivitiesResp.Activities, 1, "got activities: %v", debugPrintActivities(hostActivitiesResp.Activities))
require.NotNil(t, hostActivitiesResp.Activities[0].Details)
require.JSONEq(t, expectedActivityDetail, string(*hostActivitiesResp.Activities[0].Details))
// no MDM command got enqueued due to the /status call (device not released yet)
cmd, err = mdmDevice.Idle()
require.NoError(t, err)
@@ -2093,20 +2145,6 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
// The /setup_experience/status endpoint doesn't return the various IDs for executions, so pull
// it out manually
results, err := s.ds.ListSetupExperienceResultsByHostUUID(ctx, enrolledHost.UUID)
require.Len(t, results, 2)
require.NoError(t, err)
var installUUID string
for _, r := range results {
if r.HostSoftwareInstallsExecutionID != nil {
installUUID = *r.HostSoftwareInstallsExecutionID
}
}
require.NotEmpty(t, installUUID)
// record a result for software installation
s.Do("POST", "/api/fleet/orbit/software_install/result",
json.RawMessage(fmt.Sprintf(`{
@@ -2137,12 +2175,10 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
// Software is installed, now we should run the script
statusResp = getOrbitSetupExperienceStatusResponse{}
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *enrolledHost.OrbitNodeKey)), http.StatusOK, &statusResp)
// Software is now running, script is still pending
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
require.NotNil(t, statusResp.Results.Script)
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
require.Equal(t, fleet.SetupExperienceStatusRunning, statusResp.Results.Script.Status)
@@ -2158,6 +2194,48 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
}
}
// Validate past activity for software install
// For some reason the display name that's included in the `enrolledHost` is _slightly_
// different than the expected value in the activities. Pulling the host directly gets the
// correct display name.
var getHostResp getHostResponse
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", enrolledHost.ID), nil, http.StatusOK, &getHostResp)
expectedActivityDetail = fmt.Sprintf(`
{
"host_id": %d,
"host_display_name": "%s",
"software_title": "%s",
"software_package": "%s",
"self_service": false,
"install_uuid": "%s",
"status": "installed",
"policy_id": null,
"policy_name": null
}
`, enrolledHost.ID, getHostResp.Host.DisplayName, statusResp.Results.Software[0].Name, getSoftwareTitleResp.SoftwareTitle.SoftwarePackage.Name, installUUID)
s.lastActivityMatches(fleet.ActivityTypeInstalledSoftware{}.ActivityName(), expectedActivityDetail, 0)
// Validate upcoming activity for the script
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities/upcoming", enrolledHost.ID),
nil, http.StatusOK, &hostActivitiesResp)
expectedActivityDetail = fmt.Sprintf(`
{
"async": true,
"host_id": %d,
"policy_id": null,
"policy_name": null,
"script_name": "%s",
"host_display_name": "%s",
"script_execution_id": "%s"
}
`, enrolledHost.ID, statusResp.Results.Script.Name, enrolledHost.DisplayName(), execID)
require.Len(t, hostActivitiesResp.Activities, 1, "got activities: %v", debugPrintActivities(hostActivitiesResp.Activities))
require.NotNil(t, hostActivitiesResp.Activities[0].Details)
require.JSONEq(t, expectedActivityDetail, string(*hostActivitiesResp.Activities[0].Details))
// record a result for script execution
var scriptResp orbitPostScriptResultResponse
s.DoJSON("POST", "/api/fleet/orbit/scripts/result",
@@ -2168,7 +2246,6 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
// release of the device, as all setup experience steps are now complete.
statusResp = getOrbitSetupExperienceStatusResponse{}
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *enrolledHost.OrbitNodeKey)), http.StatusOK, &statusResp)
// Software is now running, script is still pending
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
@@ -2202,6 +2279,21 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
}
require.Equal(t, 1, deviceConfiguredCount)
require.Equal(t, 0, otherCount)
// Validate activity for script run
expectedActivityDetail = fmt.Sprintf(`
{
"async": true,
"host_id": %d,
"policy_id": null,
"policy_name": null,
"script_name": "%s",
"host_display_name": "%s",
"script_execution_id": "%s"
}
`, enrolledHost.ID, statusResp.Results.Script.Name, getHostResp.Host.DisplayName, execID)
s.lastActivityMatches(fleet.ActivityTypeRanScript{}.ActivityName(), expectedActivityDetail, 0)
}
func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptForceRelease() {
@@ -2360,7 +2452,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptFo
require.Equal(t, 0, otherCount)
}
func (s *integrationMDMTestSuite) TestReenrollingADEDeviceAfterRemovingtFromABM() {
func (s *integrationMDMTestSuite) TestReenrollingADEDeviceAfterRemovingItFromABM() {
t := s.T()
s.enableABM(t.Name())
ctx := context.Background()
@@ -2534,7 +2626,6 @@ func (s *integrationMDMTestSuite) TestReenrollingADEDeviceAfterRemovingtFromABM(
{SerialNumber: mdmDevice.SerialNumber, Model: "MacBook Pro", OS: "osx", OpType: "deleted", OpDate: time.Now()},
}
t.Log("RUN AFTER DELETED")
s.runDEPSchedule()
a := checkHostDEPAssignProfileResponses([]string{mdmDevice.SerialNumber}, profileAssignmentReqs[0].ProfileUUID, fleet.DEPAssignProfileResponseSuccess)
@@ -2550,7 +2641,6 @@ func (s *integrationMDMTestSuite) TestReenrollingADEDeviceAfterRemovingtFromABM(
{SerialNumber: mdmDevice.SerialNumber, Model: "MacBook Pro", OS: "osx", OpType: "added", OpDate: time.Now(), ProfileUUID: a[mdmDevice.SerialNumber].ProfileUUID},
}
t.Log("RUN AFTER RE-ADDED")
s.runDEPSchedule()
a = checkHostDEPAssignProfileResponses([]string{mdmDevice.SerialNumber}, profileAssignmentReqs[0].ProfileUUID, fleet.DEPAssignProfileResponseSuccess)
+10 -1
View File
@@ -837,11 +837,20 @@ func (svc *Service) SaveHostScriptResult(ctx context.Context, result *fleet.Host
}
}
var scriptName string
if hsr.ScriptID != nil {
switch {
case hsr.ScriptID != nil:
scr, err := svc.ds.Script(ctx, *hsr.ScriptID)
if err != nil {
return ctxerr.Wrap(ctx, err, "get saved script")
}
scriptName = scr.Name
case hsr.SetupExperienceScriptID != nil:
scr, err := svc.ds.GetSetupExperienceScriptByID(ctx, *hsr.SetupExperienceScriptID)
if err != nil {
return ctxerr.Wrap(ctx, err, "get setup experience script")
}
scriptName = scr.Name
}