Skip to content

Commit

Permalink
[Fleet] Changes to agent upgrade modal to allow for rolling upgrades (e…
Browse files Browse the repository at this point in the history
…lastic#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 <[email protected]>
  • Loading branch information
criamico and kibanamachine authored May 20, 2022
1 parent 473141f commit aa4c389
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 51 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/types/rest_spec/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface PostBulkAgentUpgradeRequest {
agents: string[] | string;
source_uri?: string;
version: string;
rollout_duration_seconds?: number;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
<AgentUpgradeAgentModal
agents={[agent]}
agentCount={1}
version={kibanaVersion}
onClose={() => {
setIsUpgradeModalOpen(false);
refreshAgent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
AgentUnenrollAgentModal,
AgentUpgradeAgentModal,
} from '../../components';
import { useKibanaVersion } from '../../../../hooks';

import type { SelectionMode } from './types';

Expand All @@ -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);
Expand Down Expand Up @@ -150,7 +148,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
{isUpgradeModalOpen && (
<EuiPortal>
<AgentUpgradeAgentModal
version={kibanaVersion}
agents={agents}
agentCount={agentCount}
onClose={() => {
Expand All @@ -172,7 +169,7 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
fill
iconType="arrowDown"
iconSide="right"
onClick={openMenu}
onClick={onClickMenu}
data-test-subj="agentBulkActionsButton"
>
<FormattedMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
fetchData();
refreshUpgrades();
}}
version={kibanaVersion}
/>
</EuiPortal>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -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];
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,104 @@

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
? await sendPostAgentUpgrade((agents[0] as Agent).id, {
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;
}

Expand Down Expand Up @@ -114,39 +173,20 @@ 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"
defaultMessage="Upgrade agent to latest version"
/>
) : (
<FormattedMessage
id="xpack.fleet.upgradeAgents.upgradeMultipleTitle"
defaultMessage="Upgrade {count, plural, one {agent} other {{count} agents} =true {all selected agents}} to latest version"
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."
/>
}
<>
{isSingleAgent ? (
<FormattedMessage
id="xpack.fleet.upgradeAgents.upgradeSingleTitle"
defaultMessage="Upgrade agent to latest version"
/>
) : (
<FormattedMessage
id="xpack.fleet.upgradeAgents.upgradeMultipleTitle"
defaultMessage="Upgrade {count, plural, one {agent} other {{count} agents} =true {all selected agents}} to latest version"
values={{ count: isAllAgents || agentCount }}
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
</>
}
onCancel={onClose}
onConfirm={onSubmit}
Expand Down Expand Up @@ -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>
);
};
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -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}個のエージェントをアップグレードしました",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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} 个代理",
Expand Down

0 comments on commit aa4c389

Please sign in to comment.