Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 11 commits into from
May 20, 2022
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 @@ -500,7 +500,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
setAgentToUpgrade(undefined);
fetchData();
}}
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 = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add 8.3.0 too here? or can we dynamically add the current kibana version so it's not breaking when we create a new kibana version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the version dynamically in the component.

'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