Skip to content

Commit bfaf453

Browse files
committed
[Ingest Manager] Allow to force unenroll from the UI (#72386)
1 parent b563297 commit bfaf453

5 files changed

Lines changed: 82 additions & 31 deletions

File tree

x-pack/plugins/ingest_manager/common/services/agent_status.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentSta
1313
if (!agent.active) {
1414
return 'inactive';
1515
}
16-
if (!agent.last_checkin) {
17-
return 'enrolling';
18-
}
1916
if (agent.unenrollment_started_at && !agent.unenrolled_at) {
2017
return 'unenrolling';
2118
}
19+
if (!agent.last_checkin) {
20+
return 'enrolling';
21+
}
2222

2323
const msLastCheckIn = new Date(lastCheckIn || 0).getTime();
2424
const msSinceLastCheckIn = new Date().getTime() - msLastCheckIn;

x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/context_menu_actions.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ type Props = {
2222
props: EuiButtonProps;
2323
children: JSX.Element;
2424
};
25+
isOpen?: boolean;
26+
onChange?: (isOpen: boolean) => void;
2527
} & (
2628
| {
2729
items: EuiContextMenuPanelProps['items'];
@@ -31,10 +33,22 @@ type Props = {
3133
}
3234
);
3335

34-
export const ContextMenuActions = React.memo<Props>(({ button, ...props }) => {
35-
const [isOpen, setIsOpen] = useState(false);
36-
const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]);
37-
const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]);
36+
export const ContextMenuActions = React.memo<Props>(({ button, onChange, isOpen, ...props }) => {
37+
const [isOpenState, setIsOpenState] = useState(false);
38+
const handleCloseMenu = useCallback(() => {
39+
if (onChange) {
40+
onChange(false);
41+
} else {
42+
setIsOpenState(false);
43+
}
44+
}, [setIsOpenState, onChange]);
45+
const handleToggleMenu = useCallback(() => {
46+
if (onChange) {
47+
onChange(!isOpen);
48+
} else {
49+
setIsOpenState(!isOpenState);
50+
}
51+
}, [isOpenState, onChange, isOpen]);
3852

3953
return (
4054
<EuiPopover
@@ -55,7 +69,7 @@ export const ContextMenuActions = React.memo<Props>(({ button, ...props }) => {
5569
/>
5670
)
5771
}
58-
isOpen={isOpen}
72+
isOpen={isOpen === undefined ? isOpenState : isOpen}
5973
closePopover={handleCloseMenu}
6074
>
6175
{'items' in props ? (

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
2020
const hasWriteCapabilites = useCapabilities().write;
2121
const refreshAgent = useAgentRefresh();
2222
const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(assignFlyoutOpenByDefault);
23+
const isUnenrolling = agent.status === 'unenrolling';
2324

2425
const onClose = useMemo(() => {
2526
if (onCancelReassign) {
@@ -59,7 +60,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
5960
defaultMessage="Assign new agent config"
6061
/>
6162
</EuiContextMenuItem>,
62-
<AgentUnenrollProvider key="unenrollAgent">
63+
<AgentUnenrollProvider key="unenrollAgent" forceUnenroll={isUnenrolling}>
6364
{(unenrollAgentsPrompt) => (
6465
<EuiContextMenuItem
6566
icon="cross"
@@ -68,10 +69,17 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
6869
unenrollAgentsPrompt([agent.id], 1, refreshAgent);
6970
}}
7071
>
71-
<FormattedMessage
72-
id="xpack.ingestManager.agentList.unenrollOneButton"
73-
defaultMessage="Unenroll"
74-
/>
72+
{isUnenrolling ? (
73+
<FormattedMessage
74+
id="xpack.ingestManager.agentList.forceUnenrollOneButton"
75+
defaultMessage="Force unenroll"
76+
/>
77+
) : (
78+
<FormattedMessage
79+
id="xpack.ingestManager.agentList.unenrollOneButton"
80+
defaultMessage="Unenroll"
81+
/>
82+
)}
7583
</EuiContextMenuItem>
7684
)}
7785
</AgentUnenrollProvider>,

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,12 @@ const RowActions = React.memo<{ agent: Agent; onReassignClick: () => void; refre
7474
const { getHref } = useLink();
7575
const hasWriteCapabilites = useCapabilities().write;
7676

77+
const isUnenrolling = agent.status === 'unenrolling';
78+
const [isMenuOpen, setIsMenuOpen] = useState(false);
7779
return (
7880
<ContextMenuActions
81+
isOpen={isMenuOpen}
82+
onChange={(isOpen) => setIsMenuOpen(isOpen)}
7983
items={[
8084
<EuiContextMenuItem
8185
icon="inspect"
@@ -100,21 +104,29 @@ const RowActions = React.memo<{ agent: Agent; onReassignClick: () => void; refre
100104
/>
101105
</EuiContextMenuItem>,
102106

103-
<AgentUnenrollProvider>
107+
<AgentUnenrollProvider forceUnenroll={isUnenrolling}>
104108
{(unenrollAgentsPrompt) => (
105109
<EuiContextMenuItem
106110
disabled={!hasWriteCapabilites}
107111
icon="cross"
108112
onClick={() => {
109113
unenrollAgentsPrompt([agent.id], 1, () => {
110114
refresh();
115+
setIsMenuOpen(false);
111116
});
112117
}}
113118
>
114-
<FormattedMessage
115-
id="xpack.ingestManager.agentList.unenrollOneButton"
116-
defaultMessage="Unenroll"
117-
/>
119+
{isUnenrolling ? (
120+
<FormattedMessage
121+
id="xpack.ingestManager.agentList.forceUnenrollOneButton"
122+
defaultMessage="Force unenroll"
123+
/>
124+
) : (
125+
<FormattedMessage
126+
id="xpack.ingestManager.agentList.unenrollOneButton"
127+
defaultMessage="Unenroll"
128+
/>
129+
)}
118130
</EuiContextMenuItem>
119131
)}
120132
</AgentUnenrollProvider>,

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { agentRouteService } from '../../../services';
1414

1515
interface Props {
1616
children: (unenrollAgents: UnenrollAgents) => React.ReactElement;
17+
forceUnenroll?: boolean;
1718
}
1819

1920
export type UnenrollAgents = (
@@ -24,7 +25,10 @@ export type UnenrollAgents = (
2425

2526
type OnSuccessCallback = (agentsUnenrolled: string[]) => void;
2627

27-
export const AgentUnenrollProvider: React.FunctionComponent<Props> = ({ children }) => {
28+
export const AgentUnenrollProvider: React.FunctionComponent<Props> = ({
29+
children,
30+
forceUnenroll = false,
31+
}) => {
2832
const core = useCore();
2933
const [agents, setAgents] = useState<string[] | string>([]);
3034
const [agentsCount, setAgentsCount] = useState<number>(0);
@@ -65,19 +69,24 @@ export const AgentUnenrollProvider: React.FunctionComponent<Props> = ({ children
6569
const { error } = await sendRequest<PostAgentUnenrollResponse>({
6670
path: agentRouteService.getUnenrollPath(agentId),
6771
method: 'post',
72+
body: {
73+
force: forceUnenroll,
74+
},
6875
});
6976

7077
if (error) {
7178
throw new Error(error.message);
7279
}
7380

74-
const successMessage = i18n.translate(
75-
'xpack.ingestManager.unenrollAgents.successSingleNotificationTitle',
76-
{
77-
defaultMessage: "Unenrolling agent '{id}'",
78-
values: { id: agentId },
79-
}
80-
);
81+
const successMessage = forceUnenroll
82+
? i18n.translate('xpack.ingestManager.unenrollAgents.successForceSingleNotificationTitle', {
83+
defaultMessage: "Agent '{id}' unenrolled",
84+
values: { id: agentId },
85+
})
86+
: i18n.translate('xpack.ingestManager.unenrollAgents.successSingleNotificationTitle', {
87+
defaultMessage: "Unenrolling agent '{id}'",
88+
values: { id: agentId },
89+
});
8190
core.notifications.toasts.addSuccess(successMessage);
8291

8392
if (onSuccessCallback.current) {
@@ -107,11 +116,19 @@ export const AgentUnenrollProvider: React.FunctionComponent<Props> = ({ children
107116
<EuiConfirmModal
108117
title={
109118
isSingle && !unenrollByKuery ? (
110-
<FormattedMessage
111-
id="xpack.ingestManager.unenrollAgents.confirmModal.deleteSingleTitle"
112-
defaultMessage="Unenroll agent '{id}'?"
113-
values={{ id: agents[0] }}
114-
/>
119+
forceUnenroll ? (
120+
<FormattedMessage
121+
id="xpack.ingestManager.unenrollAgents.confirmModal.forceDeleteSingleTitle"
122+
defaultMessage="Force unenroll agent '{id}'?"
123+
values={{ id: agents[0] }}
124+
/>
125+
) : (
126+
<FormattedMessage
127+
id="xpack.ingestManager.unenrollAgents.confirmModal.deleteSingleTitle"
128+
defaultMessage="Unenroll agent '{id}'?"
129+
values={{ id: agents[0] }}
130+
/>
131+
)
115132
) : (
116133
<FormattedMessage
117134
id="xpack.ingestManager.unenrollAgents.confirmModal.deleteMultipleTitle"

0 commit comments

Comments
 (0)