Skip to content

Commit

Permalink
[Fleet] Display missing warning when triggering delete package polici…
Browse files Browse the repository at this point in the history
…es actions (#194808)

Fixes #190476

## Summary
Display missing warning when triggering delete package policies actions.
Currently when no agents are enrolled this warning doesn't get
displayed.
- Also added unit tests for this action provider component

![Screenshot 2024-10-03 at 12 26
59](https://github.com/user-attachments/assets/a1fa7753-f061-4f87-a0ba-54253690903a)

Video showing the warnings in different cases. The case the was fixed is
the first one:


https://github.com/user-attachments/assets/0fe9d1d2-c6fd-4f8c-84ad-1cce20ed7eac


### Testing

- You need to have an integration policy shared between several agent
policies and those agent policies shouldn't have any agents enrolled.
- Go to integration policy page and try to delete the policy from
"delete integration"
- The warning above should appear


### Checklist
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
  • Loading branch information
criamico authored Oct 3, 2024
1 parent 7d29cb1 commit 6c3ecd7
Show file tree
Hide file tree
Showing 2 changed files with 278 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
/*
* 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.
*/

import React from 'react';

import { act, fireEvent } from '@testing-library/react';

import { EuiContextMenuItem } from '@elastic/eui';

import type { AgentPolicy, PackagePolicy } from '../types';
import { createIntegrationsTestRendererMock } from '../mock';

import { sendGetAgents, useMultipleAgentPolicies } from '../hooks';

import { PackagePolicyDeleteProvider } from './package_policy_delete_provider';

jest.mock('../hooks', () => {
return {
...jest.requireActual('../hooks'),
useMultipleAgentPolicies: jest.fn(),
useStartServices: jest.fn().mockReturnValue({
notifications: {
toasts: { addSuccess: jest.fn() },
},
}),
sendGetAgents: jest.fn(),
useConfig: jest.fn().mockReturnValue({
agents: { enabled: true },
}),
};
});

const useMultipleAgentPoliciesMock = useMultipleAgentPolicies as jest.MockedFunction<
typeof useMultipleAgentPolicies
>;
const sendGetAgentsMock = sendGetAgents as jest.MockedFunction<typeof sendGetAgents>;

function renderMenu({
agentPolicies,
packagePolicyIds,
}: {
agentPolicies: AgentPolicy[];
packagePolicyIds: string[];
}) {
const renderer = createIntegrationsTestRendererMock();

const utils = renderer.render(
<PackagePolicyDeleteProvider agentPolicies={agentPolicies}>
{(deletePackagePoliciesPrompt) => {
return (
<EuiContextMenuItem
onClick={() => {
deletePackagePoliciesPrompt(packagePolicyIds, () => {});
}}
>
Delete integration
</EuiContextMenuItem>
);
}}
</PackagePolicyDeleteProvider>
);

return { utils };
}

function createMockAgentPolicies(
props: Partial<AgentPolicy> = {},
multiple?: boolean
): AgentPolicy[] {
if (!multiple) {
return [
{
id: 'some-uuid1',
namespace: 'default',
monitoring_enabled: [],
name: 'Test Policy',
description: '',
is_preconfigured: false,
status: 'active',
is_managed: false,
revision: 1,
updated_at: '',
updated_by: 'elastic',
package_policies: [
{ name: 'integration-0001' } as PackagePolicy,
{ name: 'integration-0002' } as PackagePolicy,
],
is_protected: false,
...props,
},
];
} else {
return [
{
id: 'some-uuid1',
namespace: 'default',
monitoring_enabled: [],
name: 'Test Policy 1',
description: '',
is_preconfigured: false,
status: 'active',
is_managed: false,
revision: 1,
updated_at: '',
updated_by: 'elastic',
package_policies: [],
is_protected: false,
...props,
},
{
id: 'some-uuid2',
namespace: 'default',
monitoring_enabled: [],
name: 'Test Policy 2',
description: '',
is_preconfigured: false,
status: 'active',
is_managed: false,
revision: 1,
updated_at: '',
updated_by: 'elastic',
package_policies: [
{ name: 'integration-0001' } as PackagePolicy,
{ name: 'integration-0002' } as PackagePolicy,
],
is_protected: false,
...props,
},
];
}
}

describe('PackagePolicyDeleteProvider', () => {
it('Should show delete integrations action and cancel modal', async () => {
useMultipleAgentPoliciesMock.mockReturnValue({ canUseMultipleAgentPolicies: false });
sendGetAgentsMock.mockResolvedValue({
data: {
statusSummary: {},
items: [
{
id: 'agent123',
policy_id: 'agent-policy-1',
},
],
total: 5,
},
} as any);
const agentPolicies = createMockAgentPolicies();
const { utils } = renderMenu({
agentPolicies,
packagePolicyIds: ['integration-0001'],
});
await act(async () => {
const button = utils.getByRole('button');
fireEvent.click(button);
});
expect(utils.getByText('This action will affect 5 agents.')).toBeInTheDocument();
expect(
utils.getByText('This action can not be undone. Are you sure you wish to continue?')
).toBeInTheDocument();
expect(utils.getAllByText(/is already in use by some of your agents./).length).toBe(1);
});

it('When multiple agent policies are present and agents are enrolled show additional warnings', async () => {
sendGetAgentsMock.mockResolvedValue({
data: {
statusSummary: {},
items: [
{
id: 'agent123',
policy_id: 'agent-policy-1',
},
],
total: 5,
},
} as any);
useMultipleAgentPoliciesMock.mockReturnValue({ canUseMultipleAgentPolicies: true });
const agentPolicies = createMockAgentPolicies(undefined, true);
const { utils } = renderMenu({
agentPolicies,
packagePolicyIds: ['integration-0001'],
});
await act(async () => {
const button = utils.getByRole('button');
fireEvent.click(button);
});
expect(utils.getByText('This action will affect 5 agents.')).toBeInTheDocument();
expect(
utils.getByText('This integration is shared by multiple agent policies.')
).toBeInTheDocument();
expect(
utils.getByText('This action can not be undone. Are you sure you wish to continue?')
).toBeInTheDocument();
expect(utils.queryAllByText(/is already in use by some of your agents./).length).toBe(0);
});

it('When multiple agent policies are present and no agents are enrolled show additional warnings', async () => {
sendGetAgentsMock.mockResolvedValue({
data: {
statusSummary: {},
items: [],
total: 0,
},
} as any);
useMultipleAgentPoliciesMock.mockReturnValue({ canUseMultipleAgentPolicies: true });
const agentPolicies = createMockAgentPolicies(undefined, true);
const { utils } = renderMenu({
agentPolicies,
packagePolicyIds: ['integration-0001'],
});
await act(async () => {
const button = utils.getByRole('button');
fireEvent.click(button);
});
expect(utils.queryByText('This action will affect 5 agents.')).not.toBeInTheDocument();
expect(utils.queryAllByText(/is already in use by some of your agents./).length).toBe(0);
expect(
utils.getByText('This integration is shared by multiple agent policies.')
).toBeInTheDocument();
expect(
utils.getByText('This action can not be undone. Are you sure you wish to continue?')
).toBeInTheDocument();
});

it('When agentless should show a different set of warnings', async () => {
useMultipleAgentPoliciesMock.mockReturnValue({ canUseMultipleAgentPolicies: false });
sendGetAgentsMock.mockResolvedValue({
data: {
statusSummary: {},
items: [
{
id: 'agent123',
policy_id: 'agent-policy-1',
},
],
total: 5,
},
} as any);
const agentPolicies = createMockAgentPolicies({ supports_agentless: true });
const { utils } = renderMenu({
agentPolicies,
packagePolicyIds: ['integration-0001'],
});
await act(async () => {
const button = utils.getByRole('button');
fireEvent.click(button);
});
// utils.debug();
expect(utils.queryByText('This action will affect 5 agents.')).not.toBeInTheDocument();
expect(utils.getByText(/about to delete an integration/)).toBeInTheDocument();
expect(
utils.getByText('This action can not be undone. Are you sure you wish to continue?')
).toBeInTheDocument();
expect(utils.getAllByText(/integration will stop data ingestion./).length).toBe(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import {
sendDeletePackagePolicy,
sendDeleteAgentPolicy,
useConfig,
sendGetAgents,
useMultipleAgentPolicies,
} from '../hooks';
import { AGENTS_PREFIX } from '../../common/constants';
import type { AgentPolicy } from '../types';
import { sendGetAgents, useMultipleAgentPolicies } from '../hooks';

interface Props {
agentPolicies?: AgentPolicy[];
Expand Down Expand Up @@ -235,28 +236,29 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent<Props> = ({
buttonColor="danger"
confirmButtonDisabled={isLoading || isLoadingAgentsCount}
>
{(hasMultipleAgentPolicies || isShared) && (
<>
<EuiCallOut
color="warning"
iconType="alert"
title={
<FormattedMessage
id="xpack.fleet.deletePackagePolicy.confirmModal.warningMultipleAgentPolicies"
defaultMessage="This integration is shared by multiple agent policies."
/>
}
/>
<EuiSpacer size="m" />
</>
)}

{isLoadingAgentsCount ? (
<FormattedMessage
id="xpack.fleet.deletePackagePolicy.confirmModal.loadingAgentsCountMessage"
defaultMessage="Checking affected agents…"
/>
) : agentsCount && agentPolicies ? (
<>
{(hasMultipleAgentPolicies || isShared) && (
<>
<EuiCallOut
color="warning"
iconType="alert"
title={
<FormattedMessage
id="xpack.fleet.deletePackagePolicy.confirmModal.warningMultipleAgentPolicies"
defaultMessage="This integration is shared by multiple agent policies."
/>
}
/>
<EuiSpacer size="m" />
</>
)}
<EuiCallOut
color="danger"
title={
Expand Down

0 comments on commit 6c3ecd7

Please sign in to comment.