From 1ec8781ac69609974e943ae5a5ffeac20f6612ea Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 31 Jul 2024 11:36:33 +0200 Subject: [PATCH 1/8] add setup technology selector to add integration page --- .../components/setup_technology_selector.tsx | 128 ++++++++++++++++++ .../hooks/setup_technology.ts | 15 +- .../single_page_layout/index.tsx | 19 ++- .../package_policies_table.tsx | 2 +- .../detail/policies/package_policies.tsx | 20 ++- .../package_policy_actions_menu.tsx | 28 +++- .../fleet/server/services/package_policy.ts | 23 +++- 7 files changed, 207 insertions(+), 28 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx new file mode 100644 index 00000000000000..7d54d0f502f03b --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx @@ -0,0 +1,128 @@ +/* + * 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 { FormattedMessage } from '@kbn/i18n-react'; +import { EuiBetaBadge, EuiFormRow, EuiSpacer, EuiSuperSelect, EuiText } from '@elastic/eui'; + +import { SetupTechnology } from '../../../../../types'; + +export const SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ = 'setup-technology-selector'; + +export const SetupTechnologySelector = ({ + disabled, + setupTechnology, + onSetupTechnologyChange, +}: { + disabled: boolean; + setupTechnology: SetupTechnology; + onSetupTechnologyChange: (value: SetupTechnology) => void; +}) => { + const options = [ + { + value: SetupTechnology.AGENTLESS, + inputDisplay: ( + <> + +   + + + ), + dropdownDisplay: ( + <> + + + +   + + +

+ +

+
+ + ), + }, + { + value: SetupTechnology.AGENT_BASED, + inputDisplay: ( + + ), + dropdownDisplay: ( + <> + + + + +

+ +

+
+ + ), + }, + ]; + + return ( + <> + + + } + > + + } + onChange={onSetupTechnologyChange} + itemLayoutAlign="top" + hasDividers + fullWidth + data-test-subj={SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ} + /> + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts index cb72bfd8da245b..e9c5c04892e7f5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts @@ -92,12 +92,8 @@ export function useSetupTechnology({ isEditPage?: boolean; }) { const { cloud } = useStartServices(); - const { - isAgentlessEnabled, - isAgentlessIntegration, - isAgentlessCloudEnabled, - isAgentlessServerlessEnabled, - } = useAgentless(); + const { isAgentlessEnabled, isAgentlessCloudEnabled, isAgentlessServerlessEnabled } = + useAgentless(); // this is a placeholder for the new agent-BASED policy that will be used when the user switches from agentless to agent-based and back const newAgentBasedPolicy = useRef(newAgentPolicy); @@ -107,6 +103,7 @@ export function useSetupTechnology({ const [newAgentlessPolicy, setNewAgentlessPolicy] = useState( generateNewAgentPolicyWithDefaults({ supports_agentless: true, + monitoring_enabled: [], }) ); @@ -135,12 +132,6 @@ export function useSetupTechnology({ setNewAgentPolicy, ]); - useEffect(() => { - if (isAgentlessEnabled && packageInfo && isAgentlessIntegration(packageInfo)) { - setSelectedSetupTechnology(SetupTechnology.AGENTLESS); - } - }, [isAgentlessEnabled, isAgentlessIntegration, packageInfo]); - // tech debt: remove this useEffect when Serverless uses the Agentless API // https://github.com/elastic/security-team/issues/9781 useEffect(() => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index 7190a90d561984..99ed5592ac3ab1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -83,6 +83,7 @@ import { PostInstallGoogleCloudShellModal } from './components/cloud_security_po import { PostInstallAzureArmTemplateModal } from './components/cloud_security_posture/post_install_azure_arm_template_modal'; import { RootPrivilegesCallout } from './root_callout'; import { useAgentless } from './hooks/setup_technology'; +import { SetupTechnologySelector } from './components/setup_technology_selector'; export const StepsWithLessPadding = styled(EuiSteps)` .euiStep__content { @@ -349,7 +350,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ "'package-policy-create' and 'package-policy-replace-define-step' cannot both be registered as UI extensions" ); } - const { isAgentlessEnabled } = useAgentless(); + const { isAgentlessEnabled, isAgentlessIntegration } = useAgentless(); const { handleSetupTechnologyChange, selectedSetupTechnology } = useSetupTechnology({ newAgentPolicy, setNewAgentPolicy, @@ -397,6 +398,19 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ submitAttempted={formState === 'INVALID'} /> + {/* TODO move SetupTechnologySelector out of extensionView */} + {!extensionView && isAgentlessIntegration(packageInfo) && ( + { + handleSetupTechnologyChange(value); + // agentless doesn't need system integration + setWithSysMonitoring(value === SetupTechnology.AGENT_BASED); + }} + /> + )} + {/* Only show the out-of-box configuration step if a UI extension is NOT registered */} {!extensionView && ( = ({ (({ packagePolicy }) => { + agentPolicies: InMemoryPackagePolicyAndAgentPolicy['agentPolicies']; +}>(({ packagePolicy, agentPolicies }) => { const { getHref } = useLink(); + const policySupportsAgentless = agentPolicies?.some((policy) => policy.supports_agentless); return ( {packagePolicy.name} @@ -182,8 +188,10 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps name: i18n.translate('xpack.fleet.epm.packageDetails.integrationList.name', { defaultMessage: 'Integration policy', }), - render(_, { packagePolicy }) { - return ; + render(_, { agentPolicies, packagePolicy }) { + return ( + + ); }, }, { diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index 7dc525e8622714..a312e459a535a7 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -10,9 +10,11 @@ import { EuiContextMenuItem, EuiPortal } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { AgentPolicy, InMemoryPackagePolicy } from '../types'; -import { useAgentPolicyRefresh, useAuthz, useLink } from '../hooks'; +import { useAgentPolicyRefresh, useAuthz, useLink, useStartServices } from '../hooks'; import { policyHasFleetServer } from '../services'; +import { PLUGIN_ID, pagePathGetters } from '../constants'; + import { AgentEnrollmentFlyout } from './agent_enrollment_flyout'; import { ContextMenuActions } from './context_menu_actions'; import { DangerEuiContextMenuItem } from './danger_eui_context_menu_item'; @@ -36,6 +38,9 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); const { getHref } = useLink(); const authz = useAuthz(); + const { + application: { navigateToApp }, + } = useStartServices(); const agentPolicy = agentPolicies.length > 0 ? agentPolicies[0] : undefined; // TODO: handle multiple agent policies const canWriteIntegrationPolicies = authz.integrations.writeIntegrationPolicies; @@ -87,13 +92,23 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ : []), + ) + } > , ]; - if (!agentPolicy || !agentPolicyIsManaged) { + if (!agentPolicy || !agentPolicyIsManaged || agentPolicy.supports_agentless) { const ContextMenuItem = canWriteIntegrationPolicies ? DangerEuiContextMenuItem : EuiContextMenuItem; @@ -138,7 +153,12 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ onClick={() => { deletePackagePoliciesPrompt([packagePolicy.id], () => { setIsActionsMenuOpen(false); - refreshAgentPolicy(); + if (agentPolicy?.supports_agentless) { + // go back to all agent policies + navigateToApp(PLUGIN_ID, { path: pagePathGetters.policies_list()[1] }); + } else { + refreshAgentPolicy(); + } }); }} > diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 8ee996c69e5233..cc710db8d3daef 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -6,7 +6,7 @@ */ /* eslint-disable max-classes-per-file */ -import { omit, partition, isEqual, cloneDeep } from 'lodash'; +import { omit, partition, isEqual, cloneDeep, without } from 'lodash'; import { i18n } from '@kbn/i18n'; import semverLt from 'semver/functions/lt'; import { getFlattenedObject } from '@kbn/std'; @@ -67,6 +67,7 @@ import type { DeletePackagePoliciesResponse, PolicySecretReference, AssetsMap, + AgentPolicy, } from '../../common/types'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../constants'; import { @@ -1248,15 +1249,20 @@ class PackagePolicyClientImpl implements PackagePolicyClient { ]; const hostedAgentPolicies: string[] = []; + const agentlessAgentPolicies = []; for (const agentPolicyId of uniqueAgentPolicyIds) { try { - await validateIsNotHostedPolicy( + const agentPolicy = await validateIsNotHostedPolicy( soClient, agentPolicyId, options?.force, 'Cannot remove integrations of hosted agent policy' ); + // collect agentless agent policies to delete + if (agentPolicy.supports_agentless) { + agentlessAgentPolicies.push(agentPolicyId); + } } catch (e) { hostedAgentPolicies.push(agentPolicyId); } @@ -1331,14 +1337,21 @@ class PackagePolicyClientImpl implements PackagePolicyClient { }); } + if (agentlessAgentPolicies.length > 0) { + for (const agentPolicyId of agentlessAgentPolicies) { + await agentPolicyService.delete(soClient, esClient, agentPolicyId, { force: true }); + } + } + if (!options?.skipUnassignFromAgentPolicies) { - const uniquePolicyIdsR = [ + let uniquePolicyIdsR = [ ...new Set( result .filter((r) => r.success && r.policy_ids && r.policy_ids.length > 0) .flatMap((r) => r.policy_ids!) ), ]; + uniquePolicyIdsR = without(uniquePolicyIdsR, ...agentlessAgentPolicies); const agentPoliciesWithEndpointPackagePolicies = result.reduce((acc, cur) => { if (cur.success && cur.policy_ids && cur.package?.name === 'endpoint') { @@ -2754,7 +2767,7 @@ async function validateIsNotHostedPolicy( id: string, force = false, errorMessage?: string -) { +): Promise { const agentPolicy = await agentPolicyService.get(soClient, id, false); if (!agentPolicy) { @@ -2769,6 +2782,8 @@ async function validateIsNotHostedPolicy( errorMessage ?? `Cannot update integrations of hosted agent policy ${id}` ); } + + return agentPolicy; } export function sendUpdatePackagePolicyTelemetryEvent( From fce23ec5c812f9b6e56e59ad9e286ccc7c9ae0a9 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 31 Jul 2024 12:16:05 +0200 Subject: [PATCH 2/8] added title to disabled integration policy link --- .../package_policies/package_policies_table.tsx | 16 ++++++++++++++-- .../screens/detail/policies/package_policies.tsx | 13 +++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx index 5e518c9548b0f9..b49f7b87ffcdd3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx @@ -119,7 +119,6 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ = ({ } : { disabled: true })} > - + {value} {packagePolicy.description ? ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index 87eb9f2923d770..cbb4592c957aa7 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -66,13 +66,22 @@ const IntegrationDetailsLink = memo<{ {packagePolicy.name} From 0657ea7dec632b4fbd5d2efae02e44492475c75e Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 31 Jul 2024 13:58:19 +0200 Subject: [PATCH 3/8] fix i18n ids --- .../components/setup_technology_selector.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx index 7d54d0f502f03b..e45a9d1b466f8f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx @@ -29,7 +29,7 @@ export const SetupTechnologySelector = ({ inputDisplay: ( <>   @@ -44,7 +44,7 @@ export const SetupTechnologySelector = ({ <> @@ -57,7 +57,7 @@ export const SetupTechnologySelector = ({

@@ -69,7 +69,7 @@ export const SetupTechnologySelector = ({ value: SetupTechnology.AGENT_BASED, inputDisplay: ( ), @@ -77,14 +77,14 @@ export const SetupTechnologySelector = ({ <>

@@ -101,7 +101,7 @@ export const SetupTechnologySelector = ({ fullWidth label={ } @@ -112,7 +112,7 @@ export const SetupTechnologySelector = ({ valueOfSelected={setupTechnology} placeholder={ } From 6ac27e6d9f2a77c8e83aa99f50adb588e31b9440 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:05:37 +0200 Subject: [PATCH 4/8] Update x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx Co-authored-by: David Kilfoyle <41695641+kilfoyle@users.noreply.github.com> --- .../single_page_layout/components/setup_technology_selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx index e45a9d1b466f8f..a9477c1016b963 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/setup_technology_selector.tsx @@ -36,7 +36,7 @@ export const SetupTechnologySelector = ({ ), From e0eab069aafacbdf6a6d68f56a2603a267380ae3 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:05:50 +0200 Subject: [PATCH 5/8] Update x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx Co-authored-by: David Kilfoyle <41695641+kilfoyle@users.noreply.github.com> --- .../components/package_policies/package_policies_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx index b49f7b87ffcdd3..5e14d001055014 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx @@ -136,7 +136,7 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ 'xpack.fleet.policyDetails.packagePoliciesTable.disabledEditTitle', { defaultMessage: - 'It is not allowed to edit an agentless integration. Please add a new integration if needed.', + 'Editing an agentless integration is not supported. Add a new integration if needed.', } ) : value From d638b031c82250c60d011be98db8052163bbfab6 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 31 Jul 2024 16:15:30 +0200 Subject: [PATCH 6/8] added tests --- .../single_page_layout/index.test.tsx | 42 +++++++++++++-- .../package_policy_actions_menu.test.tsx | 53 ++++++++++++++++++- .../apis/package_policy/delete.ts | 20 +++++++ .../test/fleet_api_integration/config.base.ts | 6 +++ 4 files changed, 115 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index 4109a66a59638d..729bbb3a903ede 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -127,6 +127,7 @@ jest.mock('react-router-dom', () => ({ import { AGENTLESS_POLICY_ID } from '../../../../../../../common/constants'; import { CreatePackagePolicySinglePage } from '.'; +import { SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ } from './components/setup_technology_selector'; // mock console.debug to prevent noisy logs from console.debugs in ./index.tsx let consoleDebugMock: any; @@ -160,6 +161,7 @@ describe('When on the package policy create page', () => { function getMockPackageInfo(options?: { requiresRoot?: boolean; dataStreamRequiresRoot?: boolean; + agentlessEnabled?: boolean; }) { return { data: { @@ -181,6 +183,7 @@ describe('When on the package policy create page', () => { }, ], multiple: true, + deployment_modes: { agentless: { enabled: options?.agentlessEnabled } }, }, ], data_streams: [ @@ -802,15 +805,18 @@ describe('When on the package policy create page', () => { }); jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ agentless: true } as any); (useGetPackageInfoByKeyQuery as jest.Mock).mockReturnValue( - getMockPackageInfo({ requiresRoot: false, dataStreamRequiresRoot: false }) + getMockPackageInfo({ + requiresRoot: false, + dataStreamRequiresRoot: false, + agentlessEnabled: true, + }) ); - await act(async () => { render(); }); }); - test('should create create agent and package policy when in cloud and agentless API url is set', async () => { + test('should create agent policy and package policy when in cloud and agentless API url is set', async () => { await act(async () => { fireEvent.click(renderResult.getByText(/Save and continue/).closest('button')!); }); @@ -818,7 +824,35 @@ describe('When on the package policy create page', () => { // tech debt: this should be converted to use MSW to mock the API calls // https://github.com/elastic/security-team/issues/9816 expect(sendGetOneAgentPolicy).not.toHaveBeenCalled(); - expect(sendCreateAgentPolicy).toHaveBeenCalled(); + expect(sendCreateAgentPolicy).toHaveBeenCalledWith( + expect.objectContaining({ + monitoring_enabled: ['logs', 'metrics'], + name: 'Agent policy 1', + }), + { withSysMonitoring: true } + ); + expect(sendCreatePackagePolicy).toHaveBeenCalled(); + + await waitFor(() => { + expect(renderResult.getByText('Nginx integration added')).toBeInTheDocument(); + }); + }); + + test('should create agentless agent policy and package policy when in cloud and agentless API url is set', async () => { + fireEvent.click(renderResult.getByTestId(SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ)); + fireEvent.click(renderResult.getByText('Agentless')); + await act(async () => { + fireEvent.click(renderResult.getByText(/Save and continue/).closest('button')!); + }); + + expect(sendCreateAgentPolicy).toHaveBeenCalledWith( + expect.objectContaining({ + monitoring_enabled: [], + name: 'Agentless policy for nginx-1', + supports_agentless: true, + }), + { withSysMonitoring: false } + ); expect(sendCreatePackagePolicy).toHaveBeenCalled(); await waitFor(() => { diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx index ad241e4eb52cd3..41d81986c12b82 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.test.tsx @@ -7,12 +7,12 @@ import React from 'react'; -import { act } from '@testing-library/react'; +import { act, fireEvent } from '@testing-library/react'; import type { AgentPolicy, InMemoryPackagePolicy } from '../types'; import { createIntegrationsTestRendererMock } from '../mock'; -import { useMultipleAgentPolicies } from '../hooks'; +import { useMultipleAgentPolicies, useStartServices } from '../hooks'; import { PackagePolicyActionsMenu } from './package_policy_actions_menu'; @@ -20,6 +20,19 @@ jest.mock('../hooks', () => { return { ...jest.requireActual('../hooks'), useMultipleAgentPolicies: jest.fn(), + useStartServices: jest.fn().mockReturnValue({ + application: { + navigateToApp: jest.fn(), + }, + notifications: { + toasts: { addSuccess: jest.fn() }, + }, + }), + useLink: jest.fn().mockReturnValue({ + getHref: jest + .fn() + .mockReturnValue('/mock/app/fleet/policies/some-uuid1/edit-integration/some-uuid2'), + }), }; }); @@ -140,6 +153,32 @@ describe('PackagePolicyActionsMenu', () => { }); }); + it('Should be able to delete integration from a managed agentless policy', async () => { + const agentPolicies = createMockAgentPolicies({ is_managed: true, supports_agentless: true }); + const packagePolicy = createMockPackagePolicy(); + const { utils } = renderMenu({ agentPolicies, packagePolicy }); + await act(async () => { + expect(utils.queryByText('Delete integration')).not.toBeNull(); + }); + }); + + it('Should navigate on delete integration when having an agentless policy', async () => { + const agentPolicies = createMockAgentPolicies({ is_managed: true, supports_agentless: true }); + const packagePolicy = createMockPackagePolicy(); + const { utils } = renderMenu({ agentPolicies, packagePolicy }); + + await act(async () => { + fireEvent.click(utils.getByTestId('PackagePolicyActionsDeleteItem')); + }); + await act(async () => { + fireEvent.click(utils.getByTestId('confirmModalConfirmButton')); + }); + expect(useStartServices().application.navigateToApp as jest.Mock).toHaveBeenCalledWith( + 'fleet', + { path: '/policies' } + ); + }); + it('Should show add button if the policy is not managed and showAddAgent=true', async () => { const agentPolicies = createMockAgentPolicies(); const packagePolicy = createMockPackagePolicy({ hasUpgrade: true }); @@ -170,4 +209,14 @@ describe('PackagePolicyActionsMenu', () => { ); }); }); + + it('Should disable Edit integration when agentPolicy is agentless', async () => { + const agentPolicies = createMockAgentPolicies({ is_managed: true, supports_agentless: true }); + const packagePolicy = createMockPackagePolicy(); + const { utils } = renderMenu({ agentPolicies, packagePolicy }); + await act(async () => { + const editButton = utils.getByTestId('PackagePolicyActionsEditItem'); + expect(editButton).toHaveAttribute('disabled'); + }); + }); }); diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts b/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts index b15b84dcfbee94..15fec814a0ec9b 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts @@ -148,6 +148,26 @@ export default function (providerContext: FtrProviderContext) { .set('kbn-xsrf', 'xxxx') .expect(200); }); + + it('should delete agent policy with package policy if supports_agentless', async function () { + // update existing policy to supports_agentless + await supertest + .put(`/api/fleet/agent_policies/${agentPolicy.id}`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: agentPolicy.name, + namespace: agentPolicy.namespace, + supports_agentless: true, + }) + .expect(200); + + await supertest + .delete(`/api/fleet/package_policies/${packagePolicy.id}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + + await supertest.get(`/api/fleet/agent_policies/${agentPolicy.id}`).expect(404); + }); }); describe('Delete bulk', () => { let agentPolicy: any; diff --git a/x-pack/test/fleet_api_integration/config.base.ts b/x-pack/test/fleet_api_integration/config.base.ts index b1b2b0dcac0ade..cc689e1f3f5369 100644 --- a/x-pack/test/fleet_api_integration/config.base.ts +++ b/x-pack/test/fleet_api_integration/config.base.ts @@ -91,7 +91,13 @@ export default async function ({ readConfigFile, log }: FtrConfigProviderContext 'enableStrictKQLValidation', 'subfeaturePrivileges', 'enablePackagesStateMachine', + 'agentless', ])}`, + `--xpack.cloud.id='123456789'`, + `--xpack.fleet.agentless.api.url=https://api.agentless.url/api/v1/ess`, + `--xpack.fleet.agentless.api.tls.certificate=./config/node.crt`, + `--xpack.fleet.agentless.api.tls.key=./config/node.key`, + `--xpack.fleet.agentless.api.tls.ca=./config/ca.crt`, `--logging.loggers=${JSON.stringify([ ...getKibanaCliLoggers(xPackAPITestsConfig.get('kbnTestServer.serverArgs')), From 864a24565598e70ad675b034e9f6d59402bd1e03 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 31 Jul 2024 16:31:51 +0200 Subject: [PATCH 7/8] fix test, rename tittles --- .../package_policies_table.tsx | 28 ++++++++----------- .../detail/policies/package_policies.tsx | 2 +- .../package_policy_actions_menu.tsx | 2 +- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx index 5e14d001055014..41df6b53dae1b1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx @@ -119,6 +119,17 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ = ({ } : { disabled: true })} > - - {value} - + {value} {packagePolicy.description ? (   diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index cbb4592c957aa7..b5383df7275d68 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -73,7 +73,7 @@ const IntegrationDetailsLink = memo<{ 'xpack.fleet.epm.packageDetails.integrationList.disabledEditTitle', { defaultMessage: - 'It is not allowed to edit an agentless integration. Please add a new integration if needed.', + 'Editing an agentless integration is not supported. Add a new integration if needed.', } ), } diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index a312e459a535a7..efc2d2f8bdf07a 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -105,7 +105,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ (agentPolicy?.supports_agentless ?? false) && ( ) } From d5e37cd064cde8f4d708207250de7958cc750cbf Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Fri, 2 Aug 2024 12:50:24 +0200 Subject: [PATCH 8/8] null check --- .../fleet/public/components/package_policy_actions_menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index efc2d2f8bdf07a..82600880eaef3e 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -138,7 +138,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ //
, ]; - if (!agentPolicy || !agentPolicyIsManaged || agentPolicy.supports_agentless) { + if (!agentPolicy || !agentPolicyIsManaged || agentPolicy?.supports_agentless) { const ContextMenuItem = canWriteIntegrationPolicies ? DangerEuiContextMenuItem : EuiContextMenuItem;