mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #38536 This PR moves all logic to create new activities to activity bounded context. The old service and ActivityModule methods are not facades that route to the new activity bounded context. The facades will be removed in a subsequent PR. # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added webhook support for activity events with configurable endpoint and enable/disable settings. * Enhanced automation-initiated activity creation without requiring a user context. * Improved activity service architecture with centralized creation and management. * **Improvements** * Refactored activity creation to use a dedicated service layer for better separation of concerns. * Added support for host-specific and automation-originated activities. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
198 lines
7.8 KiB
Go
198 lines
7.8 KiB
Go
package test
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
|
|
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/storage"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// CreateHostScriptUpcomingActivity creates a host script execution request
|
|
// for the provided host. It returns the upcoming activity's execution ID.
|
|
func CreateHostScriptUpcomingActivity(t *testing.T, ds fleet.Datastore, host *fleet.Host) string {
|
|
ctx := context.Background()
|
|
hsr, err := ds.NewHostScriptExecutionRequest(ctx, &fleet.HostScriptRequestPayload{
|
|
HostID: host.ID,
|
|
ScriptContents: "echo 'a'",
|
|
})
|
|
require.NoError(t, err)
|
|
return hsr.ExecutionID
|
|
}
|
|
|
|
// SetHostScriptResult sets the result of a host script queued via
|
|
// CreateHostScriptUpcomingActivity.
|
|
func SetHostScriptResult(t *testing.T, ds fleet.Datastore, host *fleet.Host, execID string, exitCode int) {
|
|
ctx := context.Background()
|
|
_, _, err := ds.SetHostScriptExecutionResult(ctx, &fleet.HostScriptResultPayload{
|
|
HostID: host.ID, ExecutionID: execID, Output: "a", ExitCode: exitCode,
|
|
}, nil) // nil = manual script run, not policy automation
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// CreateHostSoftwareInstallUpcomingActivity creates a host software install
|
|
// execution request for the provided host. It returns the upcoming activity's
|
|
// execution ID.
|
|
func CreateHostSoftwareInstallUpcomingActivity(t *testing.T, ds fleet.Datastore, host *fleet.Host, user *fleet.User) string {
|
|
ctx := context.Background()
|
|
installer, err := fleet.NewTempFileReader(strings.NewReader("echo"), t.TempDir)
|
|
require.NoError(t, err)
|
|
installerID, _, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
|
InstallScript: "install foo",
|
|
InstallerFile: installer,
|
|
StorageID: uuid.NewString(),
|
|
Filename: "foo.pkg",
|
|
Title: uuid.NewString(),
|
|
Source: "apps",
|
|
Version: "0.0.1",
|
|
UserID: user.ID,
|
|
UninstallScript: "uninstall foo",
|
|
ValidatedLabels: &fleet.LabelIdentsWithScope{},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
execID, err := ds.InsertSoftwareInstallRequest(ctx, host.ID, installerID, fleet.HostSoftwareInstallOptions{})
|
|
require.NoError(t, err)
|
|
return execID
|
|
}
|
|
|
|
// SetHostSoftwareInstallResult sets the result of a host software install
|
|
// queued via CreateHostSoftwareInstallUpcomingActivity.
|
|
func SetHostSoftwareInstallResult(t *testing.T, ds fleet.Datastore, host *fleet.Host, execID string, exitCode int) {
|
|
ctx := context.Background()
|
|
_, err := ds.SetHostSoftwareInstallResult(ctx, &fleet.HostSoftwareInstallResultPayload{
|
|
HostID: host.ID,
|
|
InstallUUID: execID,
|
|
InstallScriptExitCode: &exitCode,
|
|
}, nil)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// CreateHostSoftwareUninstallUpcomingActivity creates a host software uninstall
|
|
// execution request for the provided host. It returns the upcoming activity's
|
|
// execution ID.
|
|
func CreateHostSoftwareUninstallUpcomingActivity(t *testing.T, ds fleet.Datastore, host *fleet.Host, user *fleet.User) string {
|
|
ctx := context.Background()
|
|
installer, err := fleet.NewTempFileReader(strings.NewReader("echo"), t.TempDir)
|
|
require.NoError(t, err)
|
|
installerID, _, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
|
InstallScript: "install foo",
|
|
InstallerFile: installer,
|
|
StorageID: uuid.NewString(),
|
|
Filename: "foo.pkg",
|
|
Title: uuid.NewString(),
|
|
Source: "apps",
|
|
Version: "0.0.1",
|
|
UserID: user.ID,
|
|
UninstallScript: "uninstall foo",
|
|
ValidatedLabels: &fleet.LabelIdentsWithScope{},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
execID := uuid.NewString()
|
|
err = ds.InsertSoftwareUninstallRequest(ctx, execID, host.ID, installerID, false)
|
|
require.NoError(t, err)
|
|
return execID
|
|
}
|
|
|
|
// SetHostSoftwareUninstallResult sets the result of a host software uninstall
|
|
// queued via CreateHostSoftwareUninstallUpcomingActivity.
|
|
func SetHostSoftwareUninstallResult(t *testing.T, ds fleet.Datastore, host *fleet.Host, execID string, exitCode int) {
|
|
ctx := context.Background()
|
|
_, _, err := ds.SetHostScriptExecutionResult(ctx, &fleet.HostScriptResultPayload{
|
|
HostID: host.ID,
|
|
ExecutionID: execID,
|
|
ExitCode: exitCode,
|
|
}, nil) // nil = manual/uninstall script, not policy automation
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// CreateHostVPPAppInstallUpcomingActivity creates a VPP app install request
|
|
// for the provided host. It returns the upcoming activity's execution ID.
|
|
// Note that test.CreateInsertGlobalVPPToken(t, ds) should be used to enable
|
|
// VPP apps (create a VPP token).
|
|
func CreateHostVPPAppInstallUpcomingActivity(t *testing.T, ds fleet.Datastore, host *fleet.Host) (execID, adamID string) {
|
|
ctx := context.Background()
|
|
// Generate a short adamID that fits within the 16 character limit
|
|
adamID = uuid.NewString()[:16]
|
|
vppApp := &fleet.VPPApp{
|
|
Name: "vpp_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: adamID, Platform: fleet.MacOSPlatform}},
|
|
BundleIdentifier: adamID,
|
|
}
|
|
_, err := ds.InsertVPPAppWithTeam(ctx, vppApp, nil)
|
|
require.NoError(t, err)
|
|
execID = uuid.NewString()
|
|
err = ds.InsertHostVPPSoftwareInstall(ctx, host.ID, vppApp.VPPAppID, execID, "event-id-1", fleet.HostSoftwareInstallOptions{})
|
|
require.NoError(t, err)
|
|
return execID, adamID
|
|
}
|
|
|
|
// SetHostVPPAppInstallResult sets the result of a VPP app install queued via
|
|
// CreateHostVPPAppInstallUpcomingActivity.
|
|
// The adamID is the one for the VPP app created by that call, and status is
|
|
// one of the Apple MDM status string (Acknowledged, Error, CommandFormatError,
|
|
// etc).
|
|
func SetHostVPPAppInstallResult(t *testing.T, ds fleet.Datastore, nanods storage.CommandAndReportResultsStore, host *fleet.Host, execID, adamID, status string, newActivityFn fleet.NewActivityFunc) {
|
|
ctx := context.Background()
|
|
nanoCtx := &mdm.Request{EnrollID: &mdm.EnrollID{ID: host.UUID}, Context: ctx}
|
|
|
|
cmdRes := &mdm.CommandResults{
|
|
CommandUUID: execID,
|
|
Status: status,
|
|
Raw: []byte(`<?xml version="1.0" encoding="UTF-8"?>`),
|
|
}
|
|
err := nanods.StoreCommandReport(nanoCtx, cmdRes)
|
|
require.NoError(t, err)
|
|
err = newActivityFn(ctx, nil, fleet.ActivityInstalledAppStoreApp{
|
|
HostID: host.ID,
|
|
AppStoreID: adamID,
|
|
CommandUUID: execID,
|
|
Status: status,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// CreateHostInHouseAppInstallUpcomingActivity creates an in-house app install
|
|
// request for the provided host. It returns the upcoming activity's execution
|
|
// ID.
|
|
func CreateHostInHouseAppInstallUpcomingActivity(t *testing.T, ds fleet.Datastore, host *fleet.Host, user *fleet.User) (execID string) {
|
|
ctx := context.Background()
|
|
rnd := uuid.NewString()
|
|
ihaID, ihaTitleID, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
|
Filename: rnd + ".ipa",
|
|
Source: "ios_apps",
|
|
Extension: "ipa",
|
|
BundleIdentifier: "com.example." + rnd,
|
|
UserID: user.ID,
|
|
ValidatedLabels: &fleet.LabelIdentsWithScope{},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
execID = uuid.NewString()
|
|
err = ds.InsertHostInHouseAppInstall(ctx, host.ID, ihaID, ihaTitleID, execID, fleet.HostSoftwareInstallOptions{})
|
|
require.NoError(t, err)
|
|
return execID
|
|
}
|
|
|
|
func SetHostInHouseAppInstallResult(t *testing.T, ds fleet.Datastore, nanods storage.CommandAndReportResultsStore, host *fleet.Host, execID, status string, newActivityFn fleet.NewActivityFunc) {
|
|
ctx := context.Background()
|
|
nanoCtx := &mdm.Request{EnrollID: &mdm.EnrollID{ID: host.UUID}, Context: ctx}
|
|
|
|
cmdRes := &mdm.CommandResults{
|
|
CommandUUID: execID,
|
|
Status: status,
|
|
Raw: []byte(`<?xml version="1.0" encoding="UTF-8"?>`),
|
|
}
|
|
err := nanods.StoreCommandReport(nanoCtx, cmdRes)
|
|
require.NoError(t, err)
|
|
err = newActivityFn(ctx, nil, fleet.ActivityTypeInstalledSoftware{
|
|
HostID: host.ID,
|
|
CommandUUID: execID,
|
|
Status: status,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|