fleet/ee/server/service/setup_experience_test.go

300 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"bytes"
"context"
"io"
"testing"
"github.com/fleetdm/fleet/v4/pkg/optjson"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/fleetdm/fleet/v4/server/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSetupExperienceNextStep(t *testing.T) {
ctx := context.Background()
ds := new(mock.Store)
svc := newTestService(t, ds)
requestedInstalls := make(map[uint][]uint)
requestedUpdateSetupExperience := []*fleet.SetupExperienceStatusResult{}
requestedScriptExecution := []*fleet.HostScriptRequestPayload{}
resetIndicators := func() {
ds.InsertSoftwareInstallRequestFuncInvoked = false
ds.InsertHostVPPSoftwareInstallFuncInvoked = false
ds.NewHostScriptExecutionRequestFuncInvoked = false
ds.UpdateSetupExperienceStatusResultFuncInvoked = false
clear(requestedInstalls)
requestedUpdateSetupExperience = []*fleet.SetupExperienceStatusResult{}
requestedScriptExecution = []*fleet.HostScriptRequestPayload{}
}
host1UUID := "123"
host1ID := uint(1)
installerID1 := uint(2)
scriptID1 := uint(3)
scriptContentID1 := uint(4)
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return &fleet.AppConfig{
MDM: fleet.MDM{
EnabledAndConfigured: true,
},
}, nil
}
ds.IsHostConnectedToFleetMDMFunc = func(ctx context.Context, host *fleet.Host) (bool, error) {
return true, nil
}
var mockListSetupExperience []*fleet.SetupExperienceStatusResult
ds.ListSetupExperienceResultsByHostUUIDFunc = func(ctx context.Context, hostUUID string) ([]*fleet.SetupExperienceStatusResult, error) {
return mockListSetupExperience, nil
}
var mockListHostsLite []*fleet.Host
ds.ListHostsLiteByUUIDsFunc = func(ctx context.Context, filter fleet.TeamFilter, uuids []string) ([]*fleet.Host, error) {
return mockListHostsLite, nil
}
ds.InsertSoftwareInstallRequestFunc = func(ctx context.Context, hostID, softwareInstallerID uint, opts fleet.HostSoftwareInstallOptions) (string, error) {
requestedInstalls[hostID] = append(requestedInstalls[hostID], softwareInstallerID)
return "install-uuid", nil
}
ds.UpdateSetupExperienceStatusResultFunc = func(ctx context.Context, status *fleet.SetupExperienceStatusResult) error {
requestedUpdateSetupExperience = append(requestedUpdateSetupExperience, status)
return nil
}
ds.NewHostScriptExecutionRequestFunc = func(ctx context.Context, request *fleet.HostScriptRequestPayload) (*fleet.HostScriptResult, error) {
requestedScriptExecution = append(requestedScriptExecution, request)
return &fleet.HostScriptResult{
ExecutionID: "script-uuid",
}, nil
}
mockListHostsLite = append(mockListHostsLite, &fleet.Host{UUID: host1UUID, ID: host1ID})
finished, err := svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.True(t, finished)
assert.False(t, ds.InsertSoftwareInstallRequestFuncInvoked)
assert.False(t, ds.InsertHostVPPSoftwareInstallFuncInvoked)
assert.False(t, ds.NewHostScriptExecutionRequestFuncInvoked)
assert.False(t, ds.UpdateSetupExperienceStatusResultFuncInvoked)
resetIndicators()
// Only installer queued
mockListSetupExperience = []*fleet.SetupExperienceStatusResult{
{
HostUUID: host1UUID,
SoftwareInstallerID: &installerID1,
Status: fleet.SetupExperienceStatusPending,
},
}
finished, err = svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.False(t, finished)
assert.True(t, ds.InsertSoftwareInstallRequestFuncInvoked)
assert.False(t, ds.InsertHostVPPSoftwareInstallFuncInvoked)
assert.False(t, ds.NewHostScriptExecutionRequestFuncInvoked)
assert.True(t, ds.UpdateSetupExperienceStatusResultFuncInvoked)
assert.Len(t, requestedInstalls, 1)
assert.Len(t, requestedUpdateSetupExperience, 1)
assert.Equal(t, "install-uuid", *requestedUpdateSetupExperience[0].HostSoftwareInstallsExecutionID)
mockListSetupExperience[0].Status = fleet.SetupExperienceStatusSuccess
finished, err = svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.True(t, finished)
resetIndicators()
// TODO VPP app queueing is better done in an integration
// test, the setup required would be too much
// Only script queued
mockListSetupExperience = []*fleet.SetupExperienceStatusResult{
{
HostUUID: host1UUID,
SetupExperienceScriptID: &scriptID1,
ScriptContentID: &scriptContentID1,
Status: fleet.SetupExperienceStatusPending,
},
}
finished, err = svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.False(t, finished)
assert.False(t, ds.InsertSoftwareInstallRequestFuncInvoked)
assert.False(t, ds.InsertHostVPPSoftwareInstallFuncInvoked)
assert.True(t, ds.NewHostScriptExecutionRequestFuncInvoked)
assert.True(t, ds.UpdateSetupExperienceStatusResultFuncInvoked)
assert.Len(t, requestedScriptExecution, 1)
assert.Len(t, requestedUpdateSetupExperience, 1)
assert.Equal(t, "script-uuid", *requestedUpdateSetupExperience[0].ScriptExecutionID)
mockListSetupExperience[0].Status = fleet.SetupExperienceStatusSuccess
finished, err = svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.True(t, finished)
resetIndicators()
// Both installer and script
mockListSetupExperience = []*fleet.SetupExperienceStatusResult{
{
HostUUID: host1UUID,
SoftwareInstallerID: &installerID1,
Status: fleet.SetupExperienceStatusPending,
},
{
HostUUID: host1UUID,
SetupExperienceScriptID: &scriptID1,
ScriptContentID: &scriptContentID1,
Status: fleet.SetupExperienceStatusPending,
},
}
// Only installer is queued
finished, err = svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.False(t, finished)
assert.True(t, ds.InsertSoftwareInstallRequestFuncInvoked)
assert.False(t, ds.InsertHostVPPSoftwareInstallFuncInvoked)
assert.False(t, ds.NewHostScriptExecutionRequestFuncInvoked)
assert.True(t, ds.UpdateSetupExperienceStatusResultFuncInvoked)
assert.Len(t, requestedInstalls, 1)
assert.Len(t, requestedScriptExecution, 0)
assert.Len(t, requestedUpdateSetupExperience, 1)
// install finished, call it again. This time script is queued
mockListSetupExperience[0].Status = fleet.SetupExperienceStatusSuccess
finished, err = svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.False(t, finished)
assert.True(t, ds.InsertSoftwareInstallRequestFuncInvoked)
assert.False(t, ds.InsertHostVPPSoftwareInstallFuncInvoked)
assert.True(t, ds.NewHostScriptExecutionRequestFuncInvoked)
assert.True(t, ds.UpdateSetupExperienceStatusResultFuncInvoked)
assert.Len(t, requestedInstalls, 1)
assert.Len(t, requestedScriptExecution, 1)
assert.Len(t, requestedUpdateSetupExperience, 2)
// both finished, now we're done
mockListSetupExperience[1].Status = fleet.SetupExperienceStatusFailure
finished, err = svc.SetupExperienceNextStep(ctx, &fleet.Host{
UUID: host1UUID,
Platform: "darwin",
})
require.NoError(t, err)
assert.True(t, finished)
}
func TestSetupExperienceSetWithManualAgentInstall(t *testing.T) {
ctx := test.UserContext(context.Background(), test.UserAdmin)
ds := new(mock.Store)
svc, baseSvc := newTestServiceWithMock(t, ds)
appConfig := fleet.AppConfig{}
team := fleet.Team{}
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return &appConfig, nil
}
ds.TeamFunc = func(ctx context.Context, tid uint) (*fleet.Team, error) {
return &team, nil
}
ds.SetSetupExperienceSoftwareTitlesFunc = func(ctx context.Context, platform string, teamID uint, titleIDs []uint) error {
return nil
}
ds.ValidateEmbeddedSecretsFunc = func(ctx context.Context, documents []string) error {
return nil
}
ds.SetSetupExperienceScriptFunc = func(ctx context.Context, script *fleet.Script) error {
return nil
}
baseSvc.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
// No manual agent install, we good
// No team
err := svc.SetSetupExperienceSoftware(ctx, "darwin", 0, []uint{1, 2})
require.NoError(t, err)
scriptReader := bytes.NewReader([]byte("hello"))
err = svc.SetSetupExperienceScript(ctx, nil, "potato.sh", scriptReader)
require.NoError(t, err)
_, _ = scriptReader.Seek(0, io.SeekStart)
// Team
err = svc.SetSetupExperienceSoftware(ctx, "darwin", 1, []uint{1, 2})
require.NoError(t, err)
err = svc.SetSetupExperienceScript(ctx, ptr.Uint(1), "potato.sh", scriptReader)
require.NoError(t, err)
_, _ = scriptReader.Seek(0, io.SeekStart)
// Manual agent install
appConfig.MDM.MacOSSetup.ManualAgentInstall = optjson.SetBool(true)
team.Config.MDM.MacOSSetup.ManualAgentInstall = optjson.SetBool(true)
// No team
err = svc.SetSetupExperienceSoftware(ctx, "darwin", 0, []uint{1, 2})
require.ErrorContains(t, err, "Couldnt add setup experience software. To add software, first disable manual_agent_install.")
err = svc.SetSetupExperienceScript(ctx, nil, "potato.sh", scriptReader)
require.ErrorContains(t, err, "Couldnt add setup experience script. To add script, first disable manual_agent_install.")
_, _ = scriptReader.Seek(0, io.SeekStart)
// Team
err = svc.SetSetupExperienceSoftware(ctx, "darwin", 1, []uint{1, 2})
require.ErrorContains(t, err, "Couldnt add setup experience software. To add software, first disable manual_agent_install.")
err = svc.SetSetupExperienceScript(ctx, ptr.Uint(1), "potato.sh", scriptReader)
require.ErrorContains(t, err, "Couldnt add setup experience script. To add script, first disable manual_agent_install.")
_, _ = scriptReader.Seek(0, io.SeekStart)
// We can still set software to none though
err = svc.SetSetupExperienceSoftware(ctx, "darwin", 0, []uint{})
require.NoError(t, err)
err = svc.SetSetupExperienceSoftware(ctx, "darwin", 1, []uint{})
require.NoError(t, err)
}