[Fleet] Changes to agent upgrade modal to allow for rolling upgrades (#132421)

* [Fleet] Changes to agent upgrade modal to allow for rolling upgrades

* Update the onSubmit logic and handle case with single agent

* Fix check

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Add option to upgrade immediately; minor fixes

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

* Add callout in modal for 400 errors

* Linter fixes

* Fix i18n error

* Address code review comments

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Cristina Amico 2022-05-20 12:24:38 +02:00 committed by GitHub
parent 473141f58b
commit aa4c389ed2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 184 additions and 51 deletions

View file

@ -89,6 +89,7 @@ export interface PostBulkAgentUpgradeRequest {
agents: string[] | string;
source_uri?: string;
version: string;
rollout_duration_seconds?: number;
};
}

View file

@ -70,7 +70,6 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
<AgentUpgradeAgentModal
agents={[agent]}
agentCount={1}
version={kibanaVersion}
onClose={() => {
setIsUpgradeModalOpen(false);
refreshAgent();

View file

@ -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<Props> = ({
selectedAgents,
refreshAgents,
}) => {
const kibanaVersion = useKibanaVersion();
// Bulk actions menu states
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
const closeMenu = () => setIsMenuOpen(false);
const openMenu = () => setIsMenuOpen(true);
const onClickMenu = () => setIsMenuOpen(!isMenuOpen);
// Actions states
const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState<boolean>(false);
@ -150,7 +148,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
{isUpgradeModalOpen && (
<EuiPortal>
<AgentUpgradeAgentModal
version={kibanaVersion}
agents={agents}
agentCount={agentCount}
onClose={() => {
@ -172,7 +169,7 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
fill
iconType="arrowDown"
iconSide="right"
onClick={openMenu}
onClick={onClickMenu}
data-test-subj="agentBulkActionsButton"
>
<FormattedMessage

View file

@ -505,7 +505,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
fetchData();
refreshUpgrades();
}}
version={kibanaVersion}
/>
</EuiPortal>
)}

View file

@ -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];

View file

@ -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<EuiComboBoxOptionOption<string>>) => version[0].value as string;
export const AgentUpgradeAgentModal: React.FunctionComponent<Props> = ({
onClose,
agents,
agentCount,
version,
}) => {
const { notifications } = useStartServices();
const kibanaVersion = useKibanaVersion();
const [isSubmitting, setIsSubmitting] = useState(false);
const [errors, setErrors] = useState<string | undefined>();
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<EuiComboBoxOptionOption<string>> = fallbackVersions.map(
(option) => ({
label: option,
value: option,
})
);
const maintainanceWindows = isSmallBatch ? [0].concat(MAINTAINANCE_VALUES) : MAINTAINANCE_VALUES;
const maintainanceOptions: Array<EuiComboBoxOptionOption<number>> = 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<Props> = ({
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,8 +173,7 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<Props> = ({
<EuiConfirmModal
data-test-subj="agentUpgradeModal"
title={
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<>
{isSingleAgent ? (
<FormattedMessage
id="xpack.fleet.upgradeAgents.upgradeSingleTitle"
@ -128,25 +186,7 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<Props> = ({
values={{ count: isAllAgents || agentCount }}
/>
)}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge
iconType="beaker"
label={
<FormattedMessage
id="xpack.fleet.upgradeAgents.experimentalLabel"
defaultMessage="Experimental"
/>
}
tooltipContent={
<FormattedMessage
id="xpack.fleet.upgradeAgents.experimentalLabelTooltip"
defaultMessage="Upgrade agent might change or be removed in a future release and is not subject to the support SLA."
/>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
</>
}
onCancel={onClose}
onConfirm={onSubmit}
@ -179,17 +219,88 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<Props> = ({
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),
}}
/>
) : (
<FormattedMessage
id="xpack.fleet.upgradeAgents.upgradeMultipleDescription"
defaultMessage="This action will upgrade multiple agents to version {version}. This action can not be undone. Are you sure you wish to continue?"
values={{ version }}
values={{ version: getVersion(selectedVersion) }}
/>
)}
</p>
<EuiSpacer size="m" />
<EuiFormRow
label={i18n.translate('xpack.fleet.upgradeAgents.chooseVersionLabel', {
defaultMessage: 'Upgrade version',
})}
fullWidth
>
<EuiComboBox
data-test-subj="agentUpgradeModal.VersionCombobox"
fullWidth
singleSelection={{ asPlainText: true }}
options={fallbackOptions}
selectedOptions={selectedVersion}
onChange={(selected: Array<EuiComboBoxOptionOption<string>>) => {
setSelectedVersion(selected);
}}
/>
</EuiFormRow>
<EuiSpacer size="m" />
{!isSingleAgent ? (
<EuiFormRow
label={
<EuiFlexGroup gutterSize="s">
<EuiFlexItem grow={false}>
{i18n.translate('xpack.fleet.upgradeAgents.maintainanceAvailableLabel', {
defaultMessage: 'Maintainance window available',
})}
</EuiFlexItem>
<EuiSpacer size="xs" />
<EuiFlexItem grow={false}>
<EuiToolTip
position="top"
content={i18n.translate(
'xpack.fleet.upgradeAgents.maintainanceAvailableTooltip',
{
defaultMessage:
'Defines the duration of time available to perform the upgrade. The agent upgrades are spread uniformly across this duration in order to avoid exhausting network resources.',
}
)}
>
<EuiIcon type="iInCircle" title="TooltipIcon" />
</EuiToolTip>
</EuiFlexItem>
</EuiFlexGroup>
}
fullWidth
>
<EuiComboBox
data-test-subj="agentUpgradeModal.MaintainanceCombobox"
fullWidth
singleSelection={{ asPlainText: true }}
options={maintainanceOptions}
selectedOptions={selectedMantainanceWindow}
onChange={(selected: Array<EuiComboBoxOptionOption<number>>) => {
setSelectedMantainanceWindow(selected);
}}
/>
</EuiFormRow>
) : null}
{errors ? (
<>
<EuiCallOut
color="danger"
title={i18n.translate('xpack.fleet.upgradeAgents.warningCallout', {
defaultMessage:
'Error upgrading the selected {count, plural, one {agent} other {{count} agents}}',
values: { count: isSingleAgent },
})}
/>
</>
) : null}
</EuiConfirmModal>
);
};

View file

@ -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",

View file

@ -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}個のエージェントをアップグレードしました",

View file

@ -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} 个代理",