diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 902b32745d0e6..4cc4a52baf8d8 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -34,6 +34,7 @@ export enum InstallStatus { installed = 'installed', notInstalled = 'not_installed', installing = 'installing', + reinstalling = 'reinstalling', uninstalling = 'uninstalling', } diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx index cf60b3d783f3e..9fc42fd56878c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_package_install.tsx @@ -29,6 +29,7 @@ interface PackageInstallItem { } type InstallPackageProps = Pick & { + isReinstall?: boolean; fromUpdate?: boolean; }; type SetPackageInstallStatusProps = Pick & PackageInstallItem; @@ -66,12 +67,22 @@ function usePackageInstall({ ); const installPackage = useCallback( - async ({ name, version, title, fromUpdate = false }: InstallPackageProps) => { + async ({ + name, + version, + title, + fromUpdate = false, + isReinstall = false, + }: InstallPackageProps) => { const currStatus = getPackageInstallStatus(name); - const newStatus = { ...currStatus, name, status: InstallStatus.installing }; + const newStatus = { + ...currStatus, + name, + status: isReinstall ? InstallStatus.reinstalling : InstallStatus.installing, + }; setPackageInstallStatus(newStatus); - const res = await sendInstallPackage(name, version); + const res = await sendInstallPackage(name, version, isReinstall); if (res.error) { if (fromUpdate) { // if there is an error during update, set it back to the previous version @@ -107,24 +118,45 @@ function usePackageInstall({ history.push(settingsPath); } - notifications.toasts.addSuccess({ - title: toMountPoint( - , - { theme$ } - ), - text: toMountPoint( - , - { theme$ } - ), - }); + if (isReinstall) { + notifications.toasts.addSuccess({ + title: toMountPoint( + , + { theme$ } + ), + text: toMountPoint( + , + { theme$ } + ), + }); + } else { + notifications.toasts.addSuccess({ + title: toMountPoint( + , + { theme$ } + ), + text: toMountPoint( + , + { theme$ } + ), + }); + } } }, [ diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 3f4886fadcc75..8955fa544cf95 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -128,6 +128,13 @@ export function Detail() { return getPackageInstallStatus(packageInfo.name).status; }, [packageInfo, getPackageInstallStatus]); + const isInstalled = useMemo( + () => + packageInstallStatus === InstallStatus.installed || + packageInstallStatus === InstallStatus.reinstalling, + [packageInstallStatus] + ); + const updateAvailable = packageInfo && 'savedObject' in packageInfo && @@ -326,7 +333,7 @@ export function Detail() { ), }, - ...(packageInstallStatus === 'installed' + ...(isInstalled ? [ { isDivider: true }, { @@ -376,15 +383,15 @@ export function Detail() { [ packageInfo, updateAvailable, - packageInstallStatus, + isInstalled, userCanInstallPackages, getHref, pkgkey, integration, agentPolicyIdFromContext, - handleAddIntegrationPolicyClick, missingSecurityConfiguration, integrationInfo?.title, + handleAddIntegrationPolicyClick, ] ); @@ -412,7 +419,7 @@ export function Detail() { }, ]; - if (canReadIntegrationPolicies && packageInstallStatus === InstallStatus.installed) { + if (canReadIntegrationPolicies && isInstalled) { tabs.push({ id: 'policies', name: ( @@ -430,7 +437,7 @@ export function Detail() { }); } - if (packageInstallStatus === InstallStatus.installed && (packageInfo.assets || CustomAssets)) { + if (isInstalled && (packageInfo.assets || CustomAssets)) { tabs.push({ id: 'assets', name: ( @@ -491,9 +498,9 @@ export function Detail() { getHref, integration, canReadIntegrationPolicies, - canReadPackageSettings, - packageInstallStatus, + isInstalled, CustomAssets, + canReadPackageSettings, showCustomTab, ]); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/reinstall_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/reinstall_button.tsx new file mode 100644 index 0000000000000..c14e4eeb2edf6 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/reinstall_button.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton } from '@elastic/eui'; +import React, { Fragment, useCallback } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { PackageInfo } from '../../../../../types'; +import { InstallStatus } from '../../../../../types'; +import { useAuthz, useGetPackageInstallStatus, useInstallPackage } from '../../../../../hooks'; + +type ReinstallationButtonProps = Pick; +export function ReinstallButton(props: ReinstallationButtonProps) { + const { name, title, version } = props; + const canInstallPackages = useAuthz().integrations.installPackages; + const installPackage = useInstallPackage(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const { status: installationStatus } = getPackageInstallStatus(name); + + const isReinstalling = installationStatus === InstallStatus.reinstalling; + + const handleClickReinstall = useCallback(() => { + installPackage({ name, version, title, isReinstall: true }); + }, [installPackage, name, title, version]); + + return canInstallPackages ? ( + + + {isReinstalling ? ( + + ) : ( + + )} + + + ) : null; +} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index a14f5e6bc490f..d8a6b6080b6f9 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -45,6 +45,7 @@ import { import { KeepPoliciesUpToDateSwitch } from '../components'; import { InstallButton } from './install_button'; +import { ReinstallButton } from './reinstall_button'; import { UpdateButton } from './update_button'; import { UninstallButton } from './uninstall_button'; @@ -344,51 +345,74 @@ export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Prop ) : ( <> -
- -

- -

-
- -

+ + + +

+ +

+ + + -

-
- - -

+ + +

-

+
+
+ {packageHasUsages && ( + + + , + }} + /> + + + )} +
+ + + + +

+ +

+
+
+ + + + +
+ +
)} - {packageHasUsages && ( -

- - , - }} - /> - -

- )} )} {hideInstallOptions && isViewingOldPackage && !isUpdating && ( diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index 7864f08d19a6e..9779e95be52dd 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -102,10 +102,13 @@ export const sendGetFileByPath = (filePath: string) => { }); }; -export const sendInstallPackage = (pkgName: string, pkgVersion: string) => { +export const sendInstallPackage = (pkgName: string, pkgVersion: string, force: boolean = false) => { return sendRequest({ path: epmRouteService.getInstallPath(pkgName, pkgVersion), method: 'post', + body: { + force, + }, }); };