diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index 7a8b7b918c1e3..886730d38f831 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -89,6 +89,7 @@ export interface PostBulkAgentUpgradeRequest { agents: string[] | string; source_uri?: string; version: string; + rollout_duration_seconds?: number; }; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx index 44e87d7fb4e63..239afe6c7e330 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx @@ -70,7 +70,6 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ { setIsUpgradeModalOpen(false); refreshAgent(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx index a2515b51814ee..e27c647e25f70 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx @@ -24,7 +24,6 @@ import { AgentUnenrollAgentModal, AgentUpgradeAgentModal, } from '../../components'; -import { useKibanaVersion } from '../../../../hooks'; import type { SelectionMode } from './types'; @@ -48,11 +47,10 @@ export const AgentBulkActions: React.FunctionComponent = ({ selectedAgents, refreshAgents, }) => { - const kibanaVersion = useKibanaVersion(); // Bulk actions menu states const [isMenuOpen, setIsMenuOpen] = useState(false); const closeMenu = () => setIsMenuOpen(false); - const openMenu = () => setIsMenuOpen(true); + const onClickMenu = () => setIsMenuOpen(!isMenuOpen); // Actions states const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(false); @@ -150,7 +148,6 @@ export const AgentBulkActions: React.FunctionComponent = ({ {isUpgradeModalOpen && ( { @@ -172,7 +169,7 @@ export const AgentBulkActions: React.FunctionComponent = ({ fill iconType="arrowDown" iconSide="right" - onClick={openMenu} + onClick={onClickMenu} data-test-subj="agentBulkActionsButton" > = () => { fetchData(); refreshUpgrades(); }} - version={kibanaVersion} /> )} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/constants.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/constants.tsx new file mode 100644 index 0000000000000..b5d8cd8f4d72d --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/constants.tsx @@ -0,0 +1,32 @@ +/* + * 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. + */ + +// Available versions for the upgrade of the Elastic Agent +// These versions are only intended to be used as a fallback +// in the event that the updated versions cannot be retrieved from the endpoint + +export const FALLBACK_VERSIONS = [ + '8.2.0', + '8.1.3', + '8.1.2', + '8.1.1', + '8.1.0', + '8.0.1', + '8.0.0', + '7.9.3', + '7.9.2', + '7.9.1', + '7.9.0', + '7.8.1', + '7.8.0', + '7.17.3', + '7.17.2', + '7.17.1', + '7.17.0', +]; + +export const MAINTAINANCE_VALUES = [1, 2, 4, 8, 12, 24, 48]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx index 72ca7a5b80fd7..2122abb5e2785 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx @@ -7,34 +7,89 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiConfirmModal, EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiConfirmModal, + EuiComboBox, + EuiFormRow, + EuiSpacer, + EuiToolTip, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, + EuiCallOut, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; + import type { Agent } from '../../../../types'; import { sendPostAgentUpgrade, sendPostBulkAgentUpgrade, useStartServices, + useKibanaVersion, } from '../../../../hooks'; +import { FALLBACK_VERSIONS, MAINTAINANCE_VALUES } from './constants'; + interface Props { onClose: () => void; agents: Agent[] | string; agentCount: number; - version: string; } +const getVersion = (version: Array>) => version[0].value as string; + export const AgentUpgradeAgentModal: React.FunctionComponent = ({ onClose, agents, agentCount, - version, }) => { const { notifications } = useStartServices(); + const kibanaVersion = useKibanaVersion(); const [isSubmitting, setIsSubmitting] = useState(false); + const [errors, setErrors] = useState(); + const isSingleAgent = Array.isArray(agents) && agents.length === 1; + const isSmallBatch = Array.isArray(agents) && agents.length > 1 && agents.length <= 10; const isAllAgents = agents === ''; + + const fallbackVersions = [kibanaVersion].concat(FALLBACK_VERSIONS); + const fallbackOptions: Array> = fallbackVersions.map( + (option) => ({ + label: option, + value: option, + }) + ); + const maintainanceWindows = isSmallBatch ? [0].concat(MAINTAINANCE_VALUES) : MAINTAINANCE_VALUES; + const maintainanceOptions: Array> = maintainanceWindows.map( + (option) => ({ + label: + option === 0 + ? i18n.translate('xpack.fleet.upgradeAgents.noMaintainanceWindowOption', { + defaultMessage: 'Immediately', + }) + : i18n.translate('xpack.fleet.upgradeAgents.hourLabel', { + defaultMessage: '{option} {count, plural, one {hour} other {hours}}', + values: { option, count: option === 1 }, + }), + value: option === 0 ? 0 : option * 3600, + }) + ); + const [selectedVersion, setSelectedVersion] = useState([fallbackOptions[0]]); + const [selectedMantainanceWindow, setSelectedMantainanceWindow] = useState([ + maintainanceOptions[0], + ]); + async function onSubmit() { + const version = getVersion(selectedVersion); + const rolloutOptions = + selectedMantainanceWindow.length > 0 && (selectedMantainanceWindow[0]?.value as number) > 0 + ? { + rollout_duration_seconds: selectedMantainanceWindow[0].value, + } + : {}; + try { setIsSubmitting(true); const { data, error } = isSingleAgent @@ -42,10 +97,14 @@ export const AgentUpgradeAgentModal: React.FunctionComponent = ({ version, }) : await sendPostBulkAgentUpgrade({ - agents: Array.isArray(agents) ? agents.map((agent) => agent.id) : agents, version, + agents: Array.isArray(agents) ? agents.map((agent) => agent.id) : agents, + ...rolloutOptions, }); if (error) { + if (error?.statusCode === 400) { + setErrors(error?.message); + } throw error; } @@ -114,39 +173,20 @@ export const AgentUpgradeAgentModal: React.FunctionComponent = ({ - - {isSingleAgent ? ( - - ) : ( - - )} - - - - } - tooltipContent={ - - } + <> + {isSingleAgent ? ( + + ) : ( + - - + )} + } onCancel={onClose} onConfirm={onSubmit} @@ -179,17 +219,88 @@ export const AgentUpgradeAgentModal: React.FunctionComponent = ({ defaultMessage="This action will upgrade the agent running on '{hostName}' to version {version}. This action can not be undone. Are you sure you wish to continue?" values={{ hostName: ((agents[0] as Agent).local_metadata.host as any).hostname, - version, + version: getVersion(selectedVersion), }} /> ) : ( )}

+ + + >) => { + setSelectedVersion(selected); + }} + /> + + + {!isSingleAgent ? ( + + + {i18n.translate('xpack.fleet.upgradeAgents.maintainanceAvailableLabel', { + defaultMessage: 'Maintainance window available', + })} + + + + + + + + + } + fullWidth + > + >) => { + setSelectedMantainanceWindow(selected); + }} + /> + + ) : null} + {errors ? ( + <> + + + ) : null}
); }; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index f211cc9fede8e..8bd7308a27a70 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -13071,8 +13071,6 @@ "xpack.fleet.upgradeAgents.cancelButtonLabel": "Annuler", "xpack.fleet.upgradeAgents.confirmMultipleButtonLabel": "Mettre à niveau {count, plural, one {l'agent} other {{count} agents} =true {tous les agents sélectionnés}}", "xpack.fleet.upgradeAgents.confirmSingleButtonLabel": "Mettre à niveau l'agent", - "xpack.fleet.upgradeAgents.experimentalLabel": "Expérimental", - "xpack.fleet.upgradeAgents.experimentalLabelTooltip": "Une modification ou une suppression de la mise à niveau de l'agent peut intervenir dans une version ultérieure. La mise à niveau n'est pas soumise à l'accord de niveau de service du support technique.", "xpack.fleet.upgradeAgents.fatalErrorNotificationTitle": "Erreur lors de la mise à niveau de {count, plural, one {l'agent} other {{count} agents} =true {tous les agents sélectionnés}}", "xpack.fleet.upgradeAgents.successMultiNotificationTitle": "{isMixed, select, true {{success} agents sur {total}} other {{isAllAgents, select, true {Tous les agents sélectionnés} other {{success}} }}} mis à niveau", "xpack.fleet.upgradeAgents.successSingleNotificationTitle": "{count} agent mis à niveau", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index eec41bfb71c81..12300057ca7ff 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13178,8 +13178,6 @@ "xpack.fleet.upgradeAgents.cancelButtonLabel": "キャンセル", "xpack.fleet.upgradeAgents.confirmMultipleButtonLabel": "{count, plural, other {{count}個のエージェント} =true {すべての選択されたエージェント}}をアップグレード", "xpack.fleet.upgradeAgents.confirmSingleButtonLabel": "エージェントをアップグレード", - "xpack.fleet.upgradeAgents.experimentalLabel": "実験的", - "xpack.fleet.upgradeAgents.experimentalLabelTooltip": "アップグレードエージェントは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", "xpack.fleet.upgradeAgents.fatalErrorNotificationTitle": "{count, plural, other {{count}個のエージェント} =true {すべての選択されたエージェント}}のアップグレードエラー", "xpack.fleet.upgradeAgents.successMultiNotificationTitle": "{isMixed, select, true {{success}/{total}個の} other {{isAllAgents, select, true {すべての選択された} other {{success}} }}}エージェントをアップグレードしました", "xpack.fleet.upgradeAgents.successSingleNotificationTitle": "{count}個のエージェントをアップグレードしました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2d7566bdd8c87..5953802b0a0a5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13202,8 +13202,6 @@ "xpack.fleet.upgradeAgents.cancelButtonLabel": "取消", "xpack.fleet.upgradeAgents.confirmMultipleButtonLabel": "升级{count, plural, one {代理} other { {count} 个代理} =true {所有选定代理}}", "xpack.fleet.upgradeAgents.confirmSingleButtonLabel": "升级代理", - "xpack.fleet.upgradeAgents.experimentalLabel": "实验性", - "xpack.fleet.upgradeAgents.experimentalLabelTooltip": "在未来的版本中可能会更改或移除升级代理,其不受支持 SLA 的约束。", "xpack.fleet.upgradeAgents.fatalErrorNotificationTitle": "升级{count, plural, one {代理} other { {count} 个代理} =true {所有选定代理}}时出错", "xpack.fleet.upgradeAgents.successMultiNotificationTitle": "已升级{isMixed, select, true { {success} 个(共 {total} 个)} other {{isAllAgents, select, true {所有选定} other { {success} 个} }}}代理", "xpack.fleet.upgradeAgents.successSingleNotificationTitle": "已升级 {count} 个代理",