Feature: Script only package e2e followup (#34271)

Co-authored-by: Carlo DiCelico <carlo@fleetdm.com>
This commit is contained in:
RachelElysia
2025-10-17 10:54:00 -04:00
committed by GitHub
co-authored by Carlo DiCelico
parent 927fd1d240
commit 1ef91fe4e3
28 changed files with 160 additions and 49 deletions
+5 -1
View File
@@ -1343,6 +1343,7 @@ This activity contains the following fields:
- "software_title": Name of the software.
- "software_package": Filename of the installer.
- "status": Status of the software installation.
- "source": Software source type (e.g., "pkg_packages", "sh_packages", "ps1_packages").
- "policy_id": ID of the policy whose failure triggered the installation. Null if no associated policy.
- "policy_name": Name of the policy whose failure triggered installation. Null if no associated policy.
@@ -1358,6 +1359,7 @@ This activity contains the following fields:
"self_service": true,
"install_uuid": "d6cffa75-b5b5-41ef-9230-15073c8a88cf",
"status": "pending",
"source": "pkg_packages",
"policy_id": 1337,
"policy_name": "Ensure 1Password is installed and up to date"
}
@@ -1374,6 +1376,7 @@ This activity contains the following fields:
- "script_execution_id": ID of the software uninstall script.
- "self_service": Whether the uninstallation was initiated by the end user from the My device UI.
- "status": Status of the software uninstallation.
- "source": Software source type (e.g., "pkg_packages", "sh_packages", "ps1_packages").
#### Example
@@ -1384,7 +1387,8 @@ This activity contains the following fields:
"software_title": "Falcon.app",
"script_execution_id": "ece8d99d-4313-446a-9af2-e152cd1bad1e",
"self_service": false,
"status": "uninstalled"
"status": "uninstalled",
"source": "pkg_packages"
}
```
+12
View File
@@ -259,12 +259,23 @@ func (svc *Service) failCancelledSetupExperienceInstalls(
// 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{
HostID: hostID,
@@ -274,6 +285,7 @@ func (svc *Service) failCancelledSetupExperienceInstalls(
InstallUUID: *r.HostSoftwareInstallsExecutionID,
Status: "failed",
SelfService: false,
Source: source,
FromSetupExperience: true,
}
err = svc.NewActivity(ctx, nil, activity)
+8 -3
View File
@@ -1707,7 +1707,12 @@ func (svc *Service) addScriptPackageMetadata(ctx context.Context, payload *fleet
payload.BundleIdentifier = ""
payload.PackageIDs = nil
payload.Extension = extension
payload.Source = "scripts"
switch extension {
case "sh":
payload.Source = "sh_packages"
case "ps1":
payload.Source = "ps1_packages"
}
platform, err := fleet.SoftwareInstallerPlatformFromExtension(extension)
if err != nil {
@@ -2413,11 +2418,11 @@ func (svc *Service) SelfServiceInstallSoftwareTitle(ctx context.Context, host *f
func packageExtensionToPlatform(ext string) string {
var requiredPlatform string
switch ext {
case ".msi", ".exe":
case ".msi", ".exe", ".ps1":
requiredPlatform = "windows"
case ".pkg", ".dmg", ".zip":
requiredPlatform = "darwin"
case ".deb", ".rpm", ".gz", ".tgz":
case ".deb", ".rpm", ".gz", ".tgz", ".sh":
requiredPlatform = "linux"
default:
return ""
@@ -400,7 +400,7 @@ func TestAddScriptPackageMetadata(t *testing.T) {
require.Equal(t, "", payload.Version)
require.Equal(t, scriptContents, payload.InstallScript)
require.Equal(t, "linux", payload.Platform)
require.Equal(t, "scripts", payload.Source)
require.Equal(t, "sh_packages", payload.Source)
require.Empty(t, payload.BundleIdentifier)
require.Empty(t, payload.PackageIDs)
require.NotEmpty(t, payload.StorageID)
@@ -430,7 +430,7 @@ func TestAddScriptPackageMetadata(t *testing.T) {
require.Equal(t, "", payload.Version)
require.Equal(t, scriptContents, payload.InstallScript)
require.Equal(t, "windows", payload.Platform)
require.Equal(t, "scripts", payload.Source)
require.Equal(t, "ps1_packages", payload.Source)
require.Empty(t, payload.BundleIdentifier)
require.Empty(t, payload.PackageIDs)
require.NotEmpty(t, payload.StorageID)
@@ -131,7 +131,7 @@ describe("SoftwareScriptDetailsModal - ModalButtons component", () => {
expect(screen.getByRole("button", { name: "Cancel" })).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Rerun" }));
expect(onRerun).toHaveBeenCalledWith(99);
expect(onRerun).toHaveBeenCalledWith(99, true);
expect(onCancel).toHaveBeenCalled();
await user.click(screen.getByRole("button", { name: "Cancel" }));
@@ -144,7 +144,7 @@ interface IModalButtonsProps {
deviceAuthToken?: string;
installResultStatus?: string;
hostSoftwareId?: number;
onRerun?: (id: number) => void;
onRerun?: (id: number, isScriptPackage: boolean) => void;
onCancel: () => void;
}
@@ -159,7 +159,7 @@ export const ModalButtons = ({
const onClickRerun = () => {
// on My Device Page, where this is relevant, both will be defined
if (onRerun && hostSoftwareId) {
onRerun(hostSoftwareId);
onRerun(hostSoftwareId, true); // isScriptPackage defined for copy changes
}
onCancel();
};
@@ -190,7 +190,7 @@ interface ISoftwareInstallDetailsProps {
hostSoftware?: IHostSoftware; // for software name when not Fleet installed (not present on activity feeds)
deviceAuthToken?: string; // My Device Page only
onCancel: () => void;
onRerun?: (id: number) => void; // My Device Page only
onRerun?: (id: number, isScriptPackage?: boolean) => void; // My Device Page only
contactUrl?: string; // My Device Page only
}
@@ -12,6 +12,7 @@ import activitiesAPI, {
import {
resolveUninstallStatus,
SoftwareInstallUninstallStatus,
SCRIPT_PACKAGE_SOURCES,
} from "interfaces/software";
import { ActivityType, IActivityDetails } from "interfaces/activity";
@@ -24,6 +25,7 @@ import Pagination from "components/Pagination";
import VppInstallDetailsModal from "components/ActivityDetails/InstallDetails/VppInstallDetailsModal";
import { SoftwareInstallDetailsModal } from "components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal";
import SoftwareScriptDetailsModal from "components/ActivityDetails/InstallDetails/SoftwareScriptDetailsModal/SoftwareScriptDetailsModal";
import SoftwareUninstallDetailsModal, {
ISWUninstallDetailsParentState,
} from "components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal";
@@ -58,6 +60,10 @@ const ActivityFeed = ({
packageInstallDetails,
setPackageInstallDetails,
] = useState<IActivityDetails | null>(null);
const [
scriptPackageDetails,
setScriptPackageDetails,
] = useState<IActivityDetails | null>(null);
const [
packageUninstallDetails,
setPackageUninstallDetails,
@@ -135,7 +141,11 @@ const ActivityFeed = ({
setShowScriptDetailsModal(true);
break;
case ActivityType.InstalledSoftware:
setPackageInstallDetails({ ...details });
if (SCRIPT_PACKAGE_SOURCES.includes(details?.source || "")) {
setScriptPackageDetails({ ...details });
} else {
setPackageInstallDetails({ ...details });
}
break;
case ActivityType.UninstalledSoftware:
setPackageUninstallDetails({
@@ -256,6 +266,12 @@ const ActivityFeed = ({
onCancel={() => setPackageInstallDetails(null)}
/>
)}
{scriptPackageDetails && (
<SoftwareScriptDetailsModal
details={scriptPackageDetails}
onCancel={() => setScriptPackageDetails(null)}
/>
)}
{packageUninstallDetails && (
<SoftwareUninstallDetailsModal
{...packageUninstallDetails}
@@ -77,7 +77,8 @@ interface IPackageFormProps {
gitopsCompatible?: boolean;
}
// application/gzip is used for .tar.gz files because browsers can't handle double-extensions correctly
const ACCEPTED_EXTENSIONS = ".pkg,.msi,.exe,.deb,.rpm,application/gzip,.tgz";
const ACCEPTED_EXTENSIONS =
".pkg,.msi,.exe,.deb,.rpm,application/gzip,.tgz,.sh,.ps1";
const PackageForm = ({
labels,
@@ -103,7 +103,11 @@ const SoftwareOptionsSelector = ({
const isPlatformIosOrIpados = platform === "ios" || platform === "ipados";
const isSelfServiceDisabled = disableOptions || isPlatformIosOrIpados;
const isAutomaticInstallDisabled =
disableOptions || isPlatformIosOrIpados || isExePackage || isTarballPackage;
disableOptions ||
isPlatformIosOrIpados ||
isExePackage ||
isTarballPackage ||
isScriptPackage;
/** Tooltip only shows when enabled or for exe/tar.gz/sh/ps1 packages */
const showAutomaticInstallTooltip =
@@ -62,6 +62,7 @@ import { IPolicy, IStoredPolicyResponse } from "interfaces/policy";
import {
isValidSoftwareAggregateStatus,
SoftwareAggregateStatus,
SCRIPT_PACKAGE_SOURCES,
} from "interfaces/software";
import { API_ALL_TEAMS_ID, ITeam } from "interfaces/team";
import { IEmptyTableProps } from "interfaces/empty_table";
@@ -2001,6 +2002,9 @@ const ManageHostsPage = ({
onClickEditLabel={onEditLabelClick}
onClickDeleteLabel={toggleDeleteLabelModal}
isLoading={isLoading}
isScriptPackage={SCRIPT_PACKAGE_SOURCES.includes(
hostsData?.software_title?.source || ""
)}
/>
{renderNoEnrollSecretBanner()}
{renderTable()}
@@ -110,6 +110,7 @@ interface IHostsFilterBlockProps {
onClickEditLabel: (evt: React.MouseEvent<HTMLButtonElement>) => void;
onClickDeleteLabel: () => void;
isLoading?: boolean;
isScriptPackage?: boolean;
}
/**
@@ -164,6 +165,7 @@ const HostsFilterBlock = ({
onClickEditLabel,
onClickDeleteLabel,
isLoading = false,
isScriptPackage,
}: IHostsFilterBlockProps) => {
const { currentUser, isOnGlobalTeam } = useContext(AppContext);
@@ -522,7 +524,7 @@ const HostsFilterBlock = ({
const renderSoftwareInstallStatusBlock = () => {
const OPTIONS = [
{ value: "installed", label: "Installed" },
{ value: "installed", label: isScriptPackage ? "Ran" : "Installed" },
{ value: "failed", label: "Failed" },
{ value: "pending", label: "Pending" },
];
@@ -17,6 +17,7 @@ import {
IHostAppStoreApp,
EnhancedSoftwareInstallUninstallStatus,
IHostSoftwareWithUiStatus,
SCRIPT_PACKAGE_SOURCES,
} from "interfaces/software";
import { IconNames } from "components/icons";
import {
@@ -55,7 +56,10 @@ interface IHostInstallerActionButtonProps {
interface IHostInstallerActionCellProps {
software: IHostSoftwareWithUiStatus;
onClickInstallAction: (softwareId: number) => void;
onClickInstallAction: (
softwareId: number,
isSoftwarePackage?: boolean
) => void;
onClickUninstallAction: () => void;
onClickOpenInstructionsAction?: () => void;
baseClass: string;
@@ -287,7 +291,12 @@ export const HostInstallerActionCell = ({
baseClass={baseClass}
tooltip={installTooltip}
disabled={installDisabled}
onClick={() => onClickInstallAction(id)}
onClick={() =>
onClickInstallAction(
id,
SCRIPT_PACKAGE_SOURCES.includes(software.source)
)
}
icon={buttonDisplayConfig.install.icon}
text={buttonDisplayConfig.install.text}
testId={`${baseClass}__install-button--test`}
@@ -434,20 +434,30 @@ const HostSoftwareLibrary = ({
}, []);
const onClickInstallAction = useCallback(
async (softwareId: number) => {
async (softwareId: number, isScriptPackage = false) => {
try {
await hostAPI.installHostSoftwarePackage(id as number, softwareId);
if (isMountedRef.current) {
onInstallOrUninstall();
}
const message = () => {
switch (true) {
case isHostOnline && isScriptPackage:
return "Script is running.";
case isHostOnline && !isScriptPackage:
return "Software is installing.";
case !isHostOnline && isScriptPackage:
return "Script will run when the host comes online.";
default:
return "Software will install when the host comes online.";
}
};
renderFlash(
"success",
<>
Software{" "}
{isHostOnline
? "is installing"
: "will install when the host comes online"}
. To see details, go to <b>Details &gt; Activity</b>.
{message()} To see details, go to <b>Details &gt; Activity</b>.
</>
);
} catch (e) {
@@ -55,7 +55,7 @@ interface IHostSWLibraryTableHeaders {
details?: ISWUninstallDetailsParentState
) => void;
onSetSelectedVPPInstallDetails: (s: IVPPHostSoftware) => void;
onClickInstallAction: (softwareId: number) => void;
onClickInstallAction: (softwareId: number, isScriptPackage?: boolean) => void;
onClickUninstallAction: (softwareId: number) => void;
isHostOnline: boolean;
hostName: string;
@@ -373,7 +373,7 @@ const getEmptyCellTooltip = (
return (
<>
{softwareName ? <b>{softwareName}</b> : "Software"} can be
{softwareName ? <b>{softwareName}</b> : "Software"} can be{" "}
{isScriptPackage ? "ran" : "installed"} on the host.
<br /> Select <b>Actions &gt; Install</b> to install.
</>
@@ -350,7 +350,7 @@ const SoftwareSelfService = ({
}, []);
const onClickInstallAction = useCallback(
async (softwareId: number) => {
async (softwareId: number, isScriptPackage = false) => {
try {
await deviceApi.installSelfServiceSoftware(deviceToken, softwareId);
if (isMountedRef.current) {
@@ -358,7 +358,10 @@ const SoftwareSelfService = ({
}
} catch (error) {
// We only show toast message if API returns an error
renderFlash("error", "Couldn't install. Please try again.");
renderFlash(
"error",
`Couldn't ${isScriptPackage ? "run" : "install"}. Please try again.`
);
}
},
[deviceToken, onInstallOrUninstall, renderFlash]
@@ -46,7 +46,7 @@ interface ISelfServiceTableHeaders {
onShowUninstallDetails: (
uninstallDetails: ISWUninstallDetailsParentState
) => void;
onClickInstallAction: (softwareId: number) => void;
onClickInstallAction: (softwareId: number, isScriptPackage?: boolean) => void;
onClickUninstallAction: (software: IHostSoftwareWithUiStatus) => void;
onClickOpenInstructionsAction: (software: IHostSoftwareWithUiStatus) => void;
}
+7 -2
View File
@@ -13,6 +13,7 @@ import {
IHostSoftware,
ISoftware,
SoftwareAggregateStatus,
SoftwareSource,
} from "interfaces/software";
import {
DiskEncryptionStatus,
@@ -35,8 +36,12 @@ export interface ISortOption {
export interface ILoadHostsResponse {
hosts: IHost[];
software: ISoftware | undefined;
software_title: { name: string; version?: string } | null | undefined; // TODO: confirm type
software?: ISoftware;
software_title?: {
name: string;
version?: string;
source?: SoftwareSource;
} | null;
munki_issue: IMunkiIssuesAggregate;
mobile_device_management_solution: IMdmSolution;
}
@@ -26,8 +26,9 @@ const getDefaultInstallScript = (fileName: string): string => {
case "rpm":
return installRPM;
case "exe":
return "";
case "tar.gz":
case "sh":
case "ps1":
return "";
default:
throw new Error(`unsupported file extension: ${extension}`);
@@ -26,8 +26,9 @@ const getDefaultUninstallScript = (fileName: string): string => {
case "rpm":
return uninstallRPM;
case "exe":
return "";
case "tar.gz":
case "sh":
case "ps1":
return "";
default:
throw new Error(`unsupported file extension: ${extension}`);
+2
View File
@@ -379,6 +379,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
'install_uuid', ua.execution_id,
'status', 'pending_install',
'self_service', ua.payload->'$.self_service' IS TRUE,
'source', COALESCE(st.source, ua.payload->>'$.source'),
'policy_id', siua.policy_id,
'policy_name', p.name
) as details,
@@ -420,6 +421,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
'script_execution_id', ua.execution_id,
'status', 'pending_uninstall',
'self_service', COALESCE(ua.payload->'$.self_service', FALSE) IS TRUE,
'source', COALESCE(st.source, ua.payload->>'$.source'),
'policy_id', siua.policy_id,
'policy_name', p.name
) as details,
+12 -4
View File
@@ -975,7 +975,7 @@ func (ds *Datastore) InsertSoftwareInstallRequest(ctx context.Context, hostID ui
const (
getInstallerStmt = `
SELECT
filename, "version", title_id, COALESCE(st.name, '[deleted title]') title_name
filename, "version", title_id, COALESCE(st.name, '[deleted title]') title_name, st.source
FROM
software_installers si
LEFT JOIN software_titles st
@@ -992,6 +992,7 @@ VALUES
'installer_filename', ?,
'version', ?,
'software_title_name', ?,
'source', ?,
'user', (SELECT JSON_OBJECT('name', name, 'email', email, 'gravatar_url', gravatar_url) FROM users WHERE id = ?)
)
)`
@@ -1021,6 +1022,7 @@ VALUES
Version string `db:"version"`
TitleID *uint `db:"title_id"`
TitleName *string `db:"title_name"`
Source *string `db:"source"`
}
if err = sqlx.GetContext(ctx, ds.reader(ctx), &installerDetails, getInstallerStmt, softwareInstallerID); err != nil {
if err == sql.ErrNoRows {
@@ -1047,6 +1049,7 @@ VALUES
installerDetails.Filename,
installerDetails.Version,
installerDetails.TitleName,
installerDetails.Source,
userID,
)
if err != nil {
@@ -1152,7 +1155,7 @@ func (ds *Datastore) runInstallerUpdateSideEffectsInTransaction(ctx context.Cont
func (ds *Datastore) InsertSoftwareUninstallRequest(ctx context.Context, executionID string, hostID uint, softwareInstallerID uint, selfService bool) error {
const (
getInstallerStmt = `SELECT title_id, COALESCE(st.name, '[deleted title]') title_name
getInstallerStmt = `SELECT title_id, COALESCE(st.name, '[deleted title]') title_name, st.source
FROM software_installers si LEFT JOIN software_titles st ON si.title_id = st.id WHERE si.id = ?`
insertUAStmt = `
@@ -1164,6 +1167,7 @@ VALUES
'installer_filename', '',
'version', 'unknown',
'software_title_name', ?,
'source', ?,
'user', (SELECT JSON_OBJECT('name', name, 'email', email, 'gravatar_url', gravatar_url) FROM users WHERE id = ?),
'self_service', ?
)
@@ -1191,6 +1195,7 @@ VALUES
var installerDetails struct {
TitleID *uint `db:"title_id"`
TitleName *string `db:"title_name"`
Source *string `db:"source"`
}
if err = sqlx.GetContext(ctx, ds.reader(ctx), &installerDetails, getInstallerStmt, softwareInstallerID); err != nil {
if err == sql.ErrNoRows {
@@ -1213,6 +1218,7 @@ VALUES
false,
executionID,
installerDetails.TitleName,
installerDetails.Source,
userID,
selfService,
)
@@ -1258,7 +1264,8 @@ SELECT
hsi.host_deleted_at,
hsi.policy_id,
hsi.created_at as created_at,
hsi.updated_at as updated_at
hsi.updated_at as updated_at,
st.source
FROM
host_software_installs hsi
LEFT JOIN software_titles st ON hsi.software_title_id = st.id
@@ -1286,7 +1293,8 @@ SELECT
NULL AS host_deleted_at,
siua.policy_id AS policy_id,
ua.created_at as created_at,
ua.updated_at as updated_at
ua.updated_at as updated_at,
st.source
FROM
upcoming_activities ua
INNER JOIN software_install_upcoming_activities siua
+14 -8
View File
@@ -1837,6 +1837,7 @@ type ActivityTypeInstalledSoftware struct {
SelfService bool `json:"self_service"`
InstallUUID string `json:"install_uuid"`
Status string `json:"status"`
Source *string `json:"source,omitempty"`
PolicyID *uint `json:"policy_id"`
PolicyName *string `json:"policy_name"`
FromSetupExperience bool `json:"-"`
@@ -1864,6 +1865,7 @@ func (a ActivityTypeInstalledSoftware) Documentation() (activity, details, detai
- "software_title": Name of the software.
- "software_package": Filename of the installer.
- "status": Status of the software installation.
- "source": Software source type (e.g., "pkg_packages", "sh_packages", "ps1_packages").
- "policy_id": ID of the policy whose failure triggered the installation. Null if no associated policy.
- "policy_name": Name of the policy whose failure triggered installation. Null if no associated policy.
`, `{
@@ -1874,18 +1876,20 @@ func (a ActivityTypeInstalledSoftware) Documentation() (activity, details, detai
"self_service": true,
"install_uuid": "d6cffa75-b5b5-41ef-9230-15073c8a88cf",
"status": "pending",
"source": "pkg_packages",
"policy_id": 1337,
"policy_name": "Ensure 1Password is installed and up to date"
}`
}
type ActivityTypeUninstalledSoftware struct {
HostID uint `json:"host_id"`
HostDisplayName string `json:"host_display_name"`
SoftwareTitle string `json:"software_title"`
ExecutionID string `json:"script_execution_id"`
SelfService bool `json:"self_service"`
Status string `json:"status"`
HostID uint `json:"host_id"`
HostDisplayName string `json:"host_display_name"`
SoftwareTitle string `json:"software_title"`
ExecutionID string `json:"script_execution_id"`
SelfService bool `json:"self_service"`
Status string `json:"status"`
Source *string `json:"source,omitempty"`
}
func (a ActivityTypeUninstalledSoftware) ActivityName() string {
@@ -1904,13 +1908,15 @@ func (a ActivityTypeUninstalledSoftware) Documentation() (activity, details, det
- "software_title": Name of the software.
- "script_execution_id": ID of the software uninstall script.
- "self_service": Whether the uninstallation was initiated by the end user from the My device UI.
- "status": Status of the software uninstallation.`, `{
- "status": Status of the software uninstallation.
- "source": Software source type (e.g., "pkg_packages", "sh_packages", "ps1_packages").`, `{
"host_id": 1,
"host_display_name": "Anna's MacBook Pro",
"software_title": "Falcon.app",
"script_execution_id": "ece8d99d-4313-446a-9af2-e152cd1bad1e",
"self_service": false,
"status": "uninstalled"
"status": "uninstalled",
"source": "pkg_packages"
}`
}
+6 -2
View File
@@ -378,6 +378,8 @@ type HostSoftwareInstallerResult struct {
SoftwareInstallerID *uint `json:"-" db:"software_installer_id"`
// SoftwarePackage is the name of the software installer package.
SoftwarePackage string `json:"software_package" db:"software_package"`
// Source is the osquery source for this software (e.g., "sh_packages", "ps1_packages").
Source *string `json:"source" db:"source"`
// HostID is the ID of the host.
HostID uint `json:"host_id" db:"host_id"`
// Status is the status of the software installer package on the host.
@@ -584,8 +586,10 @@ func SofwareInstallerSourceFromExtensionAndName(ext, name string) (string, error
return "pkg_packages", nil
case "tar.gz":
return "tgz_packages", nil
case "sh", "ps1":
return "scripts", nil
case "sh":
return "sh_packages", nil
case "ps1":
return "ps1_packages", nil
default:
return "", fmt.Errorf("unsupported file type: %s", ext)
}
+4 -4
View File
@@ -203,10 +203,10 @@ func TestSofwareInstallerSourceFromExtensionAndName(t *testing.T) {
{"tar.gz", "archive.tar.gz", "tgz_packages", false},
// New script extensions
{".sh", "script.sh", "scripts", false},
{"sh", "setup.sh", "scripts", false},
{".ps1", "script.ps1", "scripts", false},
{"ps1", "setup.ps1", "scripts", false},
{".sh", "script.sh", "sh_packages", false},
{"sh", "setup.sh", "sh_packages", false},
{".ps1", "script.ps1", "ps1_packages", false},
{"ps1", "setup.ps1", "ps1_packages", false},
// Unsupported extensions
{".zip", "archive.zip", "", true},
@@ -14201,6 +14201,7 @@ func (s *integrationEnterpriseTestSuite) TestHostSoftwareInstallResult() {
SoftwarePackage: payload.Filename,
InstallUUID: installUUIDs[0],
Status: string(fleet.SoftwareInstallFailed),
Source: ptr.String("deb_packages"),
}
s.lastActivityMatches(wantAct.ActivityName(), string(jsonMustMarshal(t, wantAct)), 0)
@@ -14225,6 +14226,7 @@ func (s *integrationEnterpriseTestSuite) TestHostSoftwareInstallResult() {
SoftwarePackage: payload2.Filename,
InstallUUID: installUUIDs[1],
Status: string(fleet.SoftwareInstallFailed),
Source: ptr.String("deb_packages"),
}
s.lastActivityOfTypeMatches(wantAct.ActivityName(), string(jsonMustMarshal(t, wantAct)), 0)
@@ -14255,6 +14257,7 @@ func (s *integrationEnterpriseTestSuite) TestHostSoftwareInstallResult() {
SoftwarePackage: payload3.Filename,
InstallUUID: installUUIDs[2],
Status: string(fleet.SoftwareInstalled),
Source: ptr.String("deb_packages"),
}
lastActID := s.lastActivityOfTypeMatches(wantAct.ActivityName(), string(jsonMustMarshal(t, wantAct)), 0)
@@ -14291,6 +14294,7 @@ func (s *integrationEnterpriseTestSuite) TestHostSoftwareInstallResult() {
SoftwarePackage: payload3.Filename,
InstallUUID: installUUIDs[2],
Status: string(fleet.SoftwareInstallFailed),
Source: ptr.String("deb_packages"),
}
s.lastActivityOfTypeMatches(wantAct.ActivityName(), string(jsonMustMarshal(t, wantAct)), 0)
@@ -16578,6 +16582,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers
"self_service": false,
"install_uuid": "%s",
"status": "installed",
"source": "apps",
"policy_id": %d,
"policy_name": "%s"
}`, host1Team1.ID, host1Team1.DisplayName(), "DummyApp", "dummy_installer.pkg", host1LastInstall.ExecutionID, policy1Team1.ID, policy1Team1.Name), 0)
@@ -16598,6 +16603,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers
"self_service": false,
"install_uuid": "%s",
"status": "%s",
"source": "deb_packages",
"policy_id": %d,
"policy_name": "%s"
}`, host2Team1.ID, host2Team1.DisplayName(), "ruby", "ruby.deb", host2LastInstall.ExecutionID, fleet.SoftwareInstallFailed, policy2Team1.ID, policy2Team1.Name), 0)
@@ -16640,6 +16646,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers
"self_service": false,
"install_uuid": "%s",
"status": "%s",
"source": "programs",
"policy_id": %f,
"policy_name": "%s"
}`, host3Team2.ID, host3Team2.DisplayName(), "Fleet osquery", "fleet-osquery.msi", host3LastInstall.ExecutionID,
@@ -17588,6 +17595,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareUploadRPM() {
SoftwarePackage: payload.Filename,
InstallUUID: installUUID,
Status: string(fleet.SoftwareInstallFailed),
Source: ptr.String("rpm_packages"),
}
s.lastActivityMatches(wantAct.ActivityName(), string(jsonMustMarshal(t, wantAct)), 0)
}
@@ -18512,6 +18520,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerOrbitDownloadFailu
SoftwarePackage: payload.Filename,
InstallUUID: swInstallExecID,
Status: string(fleet.SoftwareInstalled),
Source: ptr.String("deb_packages"),
}
s.lastActivityMatches(wantAct.ActivityName(), string(jsonMustMarshal(t, wantAct)), 0)
}
@@ -20311,6 +20320,7 @@ func (s *integrationEnterpriseTestSuite) TestSetupExperienceLinuxWithSoftware()
"install_uuid": %q,
"status": "failed",
"self_service": false,
"source": "deb_packages",
"policy_name": null,
"policy_id": null
}`, ubuntuHost.ID, ubuntuHost.DisplayName(), "vim", "vim.deb", executionIDs["vim"]), 0)
@@ -395,6 +395,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
"self_service": false,
"software_title": "%s",
"software_package": "%s",
"source": "apps",
"host_display_name": "%s"
}
`, enrolledHost.ID, installUUID, getSoftwareTitleResp.SoftwareTitle.Name, getSoftwareTitleResp.SoftwareTitle.SoftwarePackage.Name, enrolledHost.DisplayName())
@@ -484,6 +485,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
"self_service": false,
"install_uuid": "%s",
"status": "installed",
"source": "apps",
"policy_id": null,
"policy_name": null
}
+2
View File
@@ -1398,6 +1398,7 @@ func (svc *Service) SaveHostSoftwareInstallResult(ctx context.Context, result *f
SoftwarePackage: hsi.SoftwarePackage,
InstallUUID: failedExecID,
Status: string(result.Status()),
Source: hsi.Source,
SelfService: hsi.SelfService,
PolicyID: nil,
PolicyName: nil,
@@ -1480,6 +1481,7 @@ func (svc *Service) SaveHostSoftwareInstallResult(ctx context.Context, result *f
SoftwarePackage: hsi.SoftwarePackage,
InstallUUID: result.InstallUUID,
Status: string(status),
Source: hsi.Source,
SelfService: hsi.SelfService,
PolicyID: hsi.PolicyID,
PolicyName: policyName,