Skip to content

Commit

Permalink
Fleet UI: Add automatic install to custom packages upload (#24729)
Browse files Browse the repository at this point in the history
  • Loading branch information
RachelElysia authored Dec 27, 2024
1 parent 2781193 commit 5319156
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 50 deletions.
1 change: 1 addition & 0 deletions frontend/interfaces/software.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface ISoftwarePackage {
uninstall_script: string;
pre_install_query?: string;
post_install_script?: string;
automatic_install?: boolean; // POST only
self_service: boolean;
icon_url: string | null;
status: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ export const getErrorMessage = (err: unknown) => {
);
} else if (reason.includes("Secret variable")) {
return reason.replace("missing from database", "doesn't exist");
} else if (reason.includes("Unable to extract necessary metadata")) {
return (
<>
Couldn&apos;t add. Unable to extract necessary metadata.{" "}
<CustomLink
url={`${LEARN_MORE_ABOUT_BASE_LINK}/package-metadata-extraction`}
text="Learn more"
newTab
/>
</>
);
} else if (reason.includes("Fleet couldn't read the version from")) {
return (
<>
{reason}{" "}
<CustomLink
newTab
url={`${LEARN_MORE_ABOUT_BASE_LINK}/read-package-version`}
text="Learn more"
iconColor="core-fleet-white"
/>
</>
);
}

return reason || DEFAULT_ERROR_MESSAGE;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import React, { useState } from "react";

import { ILabelSummary } from "interfaces/label";
import { PackageType } from "interfaces/package_type";

import Checkbox from "components/forms/fields/Checkbox";
import TooltipWrapper from "components/TooltipWrapper";
import RevealButton from "components/buttons/RevealButton";
import Button from "components/buttons/Button";
import Radio from "components/forms/fields/Radio";
import TargetLabelSelector from "components/TargetLabelSelector";
import InfoBanner from "components/InfoBanner";
import CustomLink from "components/CustomLink";
import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";

import AdvancedOptionsFields from "pages/SoftwarePage/components/AdvancedOptionsFields";

Expand All @@ -19,13 +23,14 @@ import {

const baseClass = "fleet-app-details-form";

export type InstallType = "manual" | "automatic";
export interface IFleetMaintainedAppFormData {
selfService: boolean;
installScript: string;
preInstallQuery?: string;
postInstallScript?: string;
uninstallScript?: string;
installType: string;
installType: InstallType;
targetType: string;
customTarget: string;
labelTargets: Record<string, boolean>;
Expand All @@ -39,6 +44,7 @@ export interface IFormValidation {

interface IFleetAppDetailsFormProps {
labels: ILabelSummary[] | null;
name: string;
defaultInstallScript: string;
defaultPostInstallScript: string;
defaultUninstallScript: string;
Expand All @@ -48,8 +54,94 @@ interface IFleetAppDetailsFormProps {
onSubmit: (formData: IFleetMaintainedAppFormData) => void;
}

interface IInstallTypeSection {
installType: InstallType;
onChangeInstallType: (value: string) => void;
isCustomPackage?: boolean;
isExeCustomPackage?: boolean;
}

// Also used in custom package form (PackageForm.tsx)
export const InstallTypeSection = ({
installType,
onChangeInstallType,
isCustomPackage = false,
isExeCustomPackage = false,
}: IInstallTypeSection) => {
const isAutomaticDisabled = isExeCustomPackage;
const AUTOMATIC_DISABLED_TOOLTIP = (
<>
Fleet can&apos;t create a policy to detect existing installations for
<br /> .exe packages. To automatically install an .exe, add a custom
<br /> policy and enable the install software automation on the
<br /> <b>Policies</b> page.
</>
);

return (
<fieldset>
<legend>Install</legend>
<div className={`${baseClass}__radio-input`}>
<Radio
checked={installType === "manual"}
id="manual-radio-btn"
value="manual"
name="install-type"
label="Manual"
onChange={onChangeInstallType}
helpText="Manually install on Host details page for each host."
/>
<Radio
checked={installType === "automatic"}
id="automatic-radio-btn"
value="automatic"
name="install-type"
label="Automatic"
disabled={isAutomaticDisabled}
tooltip={isAutomaticDisabled && AUTOMATIC_DISABLED_TOOLTIP}
onChange={onChangeInstallType}
helpText={
<>
Automatically install on each host that&apos;s{" "}
<TooltipWrapper
tipContent={
<>
If the host already has any version of this
<br /> software, it won&apos;t be installed.
</>
}
>
missing this software
</TooltipWrapper>
. Policy that triggers install can be customized after software is
added.
</>
}
/>
</div>
{installType === "automatic" && isCustomPackage && (
<InfoBanner
color="yellow"
cta={
<CustomLink
url={`${LEARN_MORE_ABOUT_BASE_LINK}/automatic-software-install-policies`}
text="Learn more"
newTab
/>
}
>
Installing software over existing installations might cause issues.
Fleet&apos;s policy may not detect these existing installations.
Please create a test team in Fleet to verify a smooth installation.
</InfoBanner>
)}
</fieldset>
);
};

const FleetAppDetailsForm = ({
labels,
name: appName,
defaultInstallScript,
defaultPostInstallScript,
defaultUninstallScript,
Expand Down Expand Up @@ -107,7 +199,8 @@ const FleetAppDetailsForm = ({
};

const onChangeInstallType = (value: string) => {
const newData = { ...formData, installType: value };
const installType = value as InstallType;
const newData = { ...formData, installType };
setFormData(newData);
};

Expand Down Expand Up @@ -140,38 +233,10 @@ const FleetAppDetailsForm = ({

return (
<form className={baseClass} onSubmit={onSubmitForm}>
<fieldset>
<legend>Install</legend>
<div className={`${baseClass}__radio-inputs`}>
<Radio
checked={formData.installType === "manual"}
id="manual"
value="manual"
name="install-type"
label="Manual"
onChange={onChangeInstallType}
helpText="Manually install on Host details page for each host."
/>
<Radio
checked={formData.installType === "automatic"}
id="automatic"
value="automatic"
name="install-type"
label="Automatic"
onChange={onChangeInstallType}
helpText={
<>
Automatically install on each host that&apos;s{" "}
<TooltipWrapper tipContent="If the host already has any version of this software, it won't be installed.">
missing this software.
</TooltipWrapper>{" "}
Policy that triggers install can be customized after software is
added.
</>
}
/>
</div>
</fieldset>
<InstallTypeSection
installType={formData.installType}
onChangeInstallType={onChangeInstallType}
/>
<TargetLabelSelector
selectedTargetType={formData.targetType}
selectedCustomTarget={formData.customTarget}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.fleet-app-details-form {

&__advanced-options-section {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -29,9 +28,13 @@
}
}

&__radio-inputs {
&__radio-input {
display: flex;
flex-direction: column;
gap: $pad-small;
}

.info-banner {
margin-top: $pad-small;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ const FleetMaintainedAppDetailsPage = ({
/>
<FleetAppDetailsForm
labels={labels || []}
name={fleetApp.name}
showSchemaButton={!isSidePanelOpen}
defaultInstallScript={fleetApp.install_script}
defaultPostInstallScript={fleetApp.post_install_script}
Expand Down
6 changes: 3 additions & 3 deletions frontend/pages/SoftwarePage/SoftwarePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,16 +303,16 @@ const SoftwarePage = ({ children, router, location }: ISoftwarePageProps) => {
}
}, [currentTeamId, router]);

// NOTE: used to reset page number to 0 when modifying filters
// NOTE: Solution reused from ManageHostPage.tsx
// Used to reset page number to 0 when modifying filters
// Solution reused from ManageHostPage.tsx
useEffect(() => {
setResetPageIndex(false);
}, [queryParams, page]);

const onTeamChange = useCallback(
(teamId: number) => {
handleTeamChange(teamId);
// NOTE: used to reset page number to 0 when modifying filters
// Used to reset page number to 0 when modifying filters
setResetPageIndex(true);
},
[handleTeamChange]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import ConfirmSaveChangesModal from "../ConfirmSaveChangesModal";

const baseClass = "edit-software-modal";

// Install type used on add but not edit
export type IEditPackageFormData = Omit<IPackageFormData, "installType">;

interface IEditSoftwareModalProps {
softwareId: number;
teamId: number;
Expand All @@ -57,7 +60,7 @@ const EditSoftwareModal = ({
showConfirmSaveChangesModal,
setShowConfirmSaveChangesModal,
] = useState(false);
const [pendingUpdates, setPendingUpdates] = useState<IPackageFormData>({
const [pendingUpdates, setPendingUpdates] = useState<IEditPackageFormData>({
software: null,
installScript: "",
selfService: false,
Expand Down Expand Up @@ -121,7 +124,7 @@ const EditSoftwareModal = ({
setShowConfirmSaveChangesModal(!showConfirmSaveChangesModal);
};

const onSaveSoftwareChanges = async (formData: IPackageFormData) => {
const onSaveSoftwareChanges = async (formData: IEditPackageFormData) => {
setIsUpdatingSoftware(true);

if (formData.software && formData.software.size > MAX_FILE_SIZE_BYTES) {
Expand Down
Loading

0 comments on commit 5319156

Please sign in to comment.