From 3412a60c65ea85b9fe8017ec78656ab168c9e0d9 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 1 Oct 2024 13:40:25 -0700 Subject: [PATCH 01/20] Move useAgentless to top-level hook --- ...s_policy_helper.ts => agentless_helper.ts} | 15 +++ .../components/package_policy_input_panel.tsx | 2 +- .../single_page_layout/components/layout.tsx | 3 +- .../single_page_layout/hooks/form.tsx | 3 +- .../hooks/setup_technology.test.ts | 107 +-------------- .../hooks/setup_technology.ts | 68 +--------- .../single_page_layout/index.tsx | 3 +- .../edit_package_policy_page/index.tsx | 3 +- x-pack/plugins/fleet/public/hooks/index.ts | 1 + .../fleet/public/hooks/use_agentless.test.ts | 127 ++++++++++++++++++ .../fleet/public/hooks/use_agentless.ts | 56 ++++++++ .../routes/package_policy/utils/index.ts | 2 +- 12 files changed, 212 insertions(+), 178 deletions(-) rename x-pack/plugins/fleet/common/services/{agentless_policy_helper.ts => agentless_helper.ts} (50%) create mode 100644 x-pack/plugins/fleet/public/hooks/use_agentless.test.ts create mode 100644 x-pack/plugins/fleet/public/hooks/use_agentless.ts diff --git a/x-pack/plugins/fleet/common/services/agentless_policy_helper.ts b/x-pack/plugins/fleet/common/services/agentless_helper.ts similarity index 50% rename from x-pack/plugins/fleet/common/services/agentless_policy_helper.ts rename to x-pack/plugins/fleet/common/services/agentless_helper.ts index ede0dfa497187..60796f0719890 100644 --- a/x-pack/plugins/fleet/common/services/agentless_policy_helper.ts +++ b/x-pack/plugins/fleet/common/services/agentless_helper.ts @@ -5,6 +5,21 @@ * 2.0. */ +import type { PackageInfo } from '../types'; + +export const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => { + if ( + packageInfo?.policy_templates && + packageInfo?.policy_templates.length > 0 && + !!packageInfo?.policy_templates.find( + (policyTemplate) => policyTemplate?.deployment_modes?.agentless.enabled === true + ) + ) { + return true; + } + return false; +}; + export const getAgentlessAgentPolicyNameFromPackagePolicyName = (packagePolicyName: string) => { return `Agentless policy for ${packagePolicyName}`; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx index 0661c2f36a9b3..66e115c06ec2f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx @@ -31,7 +31,7 @@ import type { } from '../../../../../../types'; import type { PackagePolicyInputValidationResults } from '../../../services'; import { hasInvalidButRequiredVar, countValidationErrors } from '../../../services'; -import { useAgentless } from '../../../single_page_layout/hooks/setup_technology'; +import { useAgentless } from '../../../../../../hooks'; import { PackagePolicyInputConfig } from './package_policy_input_config'; import { PackagePolicyInputStreamConfig } from './package_policy_input_stream'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx index c5170d33eee68..910c27e8df66d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx @@ -19,8 +19,7 @@ import { EuiSpacer, } from '@elastic/eui'; -import { useAgentless } from '../hooks/setup_technology'; - +import { useAgentless } from '../../../../../hooks'; import { WithHeaderLayout } from '../../../../../layouts'; import type { AgentPolicy, PackageInfo, RegistryPolicyTemplate } from '../../../../../types'; import { PackageIcon } from '../../../../../components'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 2bae962f48e7c..0f2c230233fdd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -27,6 +27,7 @@ import { sendBulkInstallPackages, sendGetPackagePolicies, useMultipleAgentPolicies, + useAgentless, } from '../../../../../hooks'; import { isVerificationError, packageToPackagePolicy } from '../../../../../services'; import { @@ -49,8 +50,6 @@ import { getCloudShellUrlFromPackagePolicy, } from '../../../../../../../components/cloud_security_posture/services'; -import { useAgentless } from './setup_technology'; - export async function createAgentPolicy({ packagePolicy, newAgentPolicy, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts index be7884aad753d..260a5a80ed60a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts @@ -17,7 +17,7 @@ import { sendGetOneAgentPolicy, useStartServices, useConfig } from '../../../../ import { SelectedPolicyTab } from '../../components'; import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; -import { useAgentless, useSetupTechnology } from './setup_technology'; +import { useSetupTechnology } from './setup_technology'; jest.mock('../../../../../services'); jest.mock('../../../../../hooks', () => ({ @@ -30,110 +30,7 @@ jest.mock('../../../../../../../../common/services/generate_new_agent_policy'); type MockFn = jest.MockedFunction; -describe('useAgentless', () => { - const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService); - - beforeEach(() => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); - (useConfig as MockFn).mockReturnValue({ - agentless: undefined, - } as any); - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: false, - isCloudEnabled: false, - }, - }); - jest.clearAllMocks(); - }); - - it('should not return isAgentless when agentless is not enabled', () => { - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeFalsy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); - - it('should return isAgentlessEnabled as falsy if agentless.enabled is true and experimental feature agentless is truthy without cloud or serverless', () => { - (useConfig as MockFn).mockReturnValue({ - agentless: { - enabled: true, - }, - } as any); - - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeFalsy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); - - it('should return isAgentlessEnabled and isAgentlessApiEnabled as truthy with isCloudEnabled', () => { - (useConfig as MockFn).mockReturnValue({ - agentless: { - enabled: true, - }, - } as any); - - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: false, - isCloudEnabled: true, - }, - }); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeTruthy(); - expect(result.current.isAgentlessApiEnabled).toBeTruthy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); - it('should return isAgentlessEnabled and isDefaultAgentlessPolicyEnabled as truthy with isServerlessEnabled and experimental feature agentless is truthy', () => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: true, - } as any); - - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: true, - isCloudEnabled: false, - }, - }); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeTruthy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeTruthy(); - }); - - it('should return isAgentlessEnabled as falsy and isDefaultAgentlessPolicyEnabled as falsy with isServerlessEnabled and experimental feature agentless is falsy', () => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); - - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: true, - isCloudEnabled: false, - }, - }); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeFalsy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); -}); - +// TODO: Fix mocks describe('useSetupTechnology', () => { const setNewAgentPolicy = jest.fn(); const updateAgentPoliciesMock = jest.fn(); 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 fb6aefcf7583e..4cc7b40250712 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 @@ -7,77 +7,20 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { useConfig } from '../../../../../hooks'; -import { ExperimentalFeaturesService } from '../../../../../services'; -import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; -import type { - AgentPolicy, - NewAgentPolicy, - NewPackagePolicy, - PackageInfo, -} from '../../../../../types'; +import type { AgentPolicy, NewAgentPolicy, NewPackagePolicy } from '../../../../../types'; import { SetupTechnology } from '../../../../../types'; -import { sendGetOneAgentPolicy, useStartServices } from '../../../../../hooks'; +import { sendGetOneAgentPolicy } from '../../../../../hooks'; import { SelectedPolicyTab } from '../../components'; +import { useAgentless } from '../../../../../../../hooks'; import { AGENTLESS_POLICY_ID } from '../../../../../../../../common/constants'; -import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../../../../../common/services/agentless_policy_helper'; - -export const useAgentless = () => { - const config = useConfig(); - const { agentless: agentlessExperimentalFeatureEnabled } = ExperimentalFeaturesService.get(); - const { cloud } = useStartServices(); - const isServerless = !!cloud?.isServerlessEnabled; - const isCloud = !!cloud?.isCloudEnabled; - - const isAgentlessApiEnabled = (isCloud || isServerless) && config.agentless?.enabled; - const isDefaultAgentlessPolicyEnabled = - !isAgentlessApiEnabled && isServerless && agentlessExperimentalFeatureEnabled; - - const isAgentlessEnabled = isAgentlessApiEnabled || isDefaultAgentlessPolicyEnabled; - - const isAgentlessAgentPolicy = (agentPolicy: AgentPolicy | undefined) => { - if (!agentPolicy) return false; - return ( - isAgentlessEnabled && - (agentPolicy?.id === AGENTLESS_POLICY_ID || !!agentPolicy?.supports_agentless) - ); - }; - - // When an integration has at least a policy template enabled for agentless - const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => { - if ( - isAgentlessEnabled && - packageInfo?.policy_templates && - packageInfo?.policy_templates.length > 0 && - !!packageInfo?.policy_templates.find( - (policyTemplate) => policyTemplate?.deployment_modes?.agentless.enabled === true - ) - ) { - return true; - } - return false; - }; - - // TODO: remove this check when CSPM implements the above flag and rely only on `isAgentlessIntegration` - const isAgentlessPackagePolicy = (packagePolicy: NewPackagePolicy) => { - return isAgentlessEnabled && packagePolicy.policy_ids.includes(AGENTLESS_POLICY_ID); - }; - return { - isAgentlessApiEnabled, - isDefaultAgentlessPolicyEnabled, - isAgentlessEnabled, - isAgentlessAgentPolicy, - isAgentlessIntegration, - isAgentlessPackagePolicy, - }; -}; +import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; +import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../../../../../common/services/agentless_helper'; export function useSetupTechnology({ setNewAgentPolicy, newAgentPolicy, updateAgentPolicies, setSelectedPolicyTab, - packageInfo, packagePolicy, isEditPage, agentPolicies, @@ -86,7 +29,6 @@ export function useSetupTechnology({ newAgentPolicy: NewAgentPolicy; updateAgentPolicies: (policies: AgentPolicy[]) => void; setSelectedPolicyTab: (tab: SelectedPolicyTab) => void; - packageInfo?: PackageInfo; packagePolicy: NewPackagePolicy; isEditPage?: boolean; agentPolicies?: AgentPolicy[]; 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 3070b0961ab6d..96640aa28764d 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 @@ -51,6 +51,7 @@ import { useStartServices, useUIExtension, useAuthz, + useAgentless, } from '../../../../hooks'; import { DevtoolsRequestFlyoutButton, @@ -82,7 +83,6 @@ import { PostInstallCloudFormationModal } from './components/cloud_security_post import { PostInstallGoogleCloudShellModal } from './components/cloud_security_posture/post_install_google_cloud_shell_modal'; 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)` @@ -356,7 +356,6 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ setNewAgentPolicy, updateAgentPolicies, setSelectedPolicyTab, - packageInfo, packagePolicy, }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index e448d1376b2fe..74c175bf598b7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -27,6 +27,7 @@ import { useUIExtension, sendGetAgentStatus, useAuthz, + useAgentless, } from '../../../hooks'; import { useBreadcrumbs as useIntegrationsBreadcrumbs, @@ -60,8 +61,6 @@ import { RootPrivilegesCallout } from '../create_package_policy_page/single_page import { StepsWithLessPadding } from '../create_package_policy_page/single_page_layout'; -import { useAgentless } from '../create_package_policy_page/single_page_layout/hooks/setup_technology'; - import { UpgradeStatusCallout } from './components'; import { usePackagePolicyWithRelatedData, useHistoryBlock } from './hooks'; import { getNewSecrets } from './utils'; diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index f537698897a19..6452415f2159b 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -35,3 +35,4 @@ export * from './use_locator'; export * from './use_agent_version'; export * from './use_fleet_server_agents'; export * from './use_multiple_agent_policies'; +export * from './use_agentless'; diff --git a/x-pack/plugins/fleet/public/hooks/use_agentless.test.ts b/x-pack/plugins/fleet/public/hooks/use_agentless.test.ts new file mode 100644 index 0000000000000..8558389f13266 --- /dev/null +++ b/x-pack/plugins/fleet/public/hooks/use_agentless.test.ts @@ -0,0 +1,127 @@ +/* + * 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 { renderHook } from '@testing-library/react-hooks'; + +import { ExperimentalFeaturesService } from '../services'; + +import { useStartServices, useConfig } from '.'; +import { useAgentless } from './use_agentless'; + +jest.mock('../services'); +jest.mock('./use_config', () => ({ + useConfig: jest.fn(), +})); +jest.mock('./use_core', () => ({ + useStartServices: jest.fn(), +})); + +type MockFn = jest.MockedFunction; + +describe('useAgentless', () => { + const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService); + + beforeEach(() => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + (useConfig as MockFn).mockReturnValue({ + agentless: undefined, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: false, + isCloudEnabled: false, + }, + }); + jest.clearAllMocks(); + }); + + it('should not return isAgentless when agentless is not enabled', () => { + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeFalsy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); + + it('should return isAgentlessEnabled as falsy if agentless.enabled is true and experimental feature agentless is truthy without cloud or serverless', () => { + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + }, + } as any); + + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeFalsy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); + + it('should return isAgentlessEnabled and isAgentlessApiEnabled as truthy with isCloudEnabled', () => { + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + }, + } as any); + + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: false, + isCloudEnabled: true, + }, + }); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeTruthy(); + expect(result.current.isAgentlessApiEnabled).toBeTruthy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); + it('should return isAgentlessEnabled and isDefaultAgentlessPolicyEnabled as truthy with isServerlessEnabled and experimental feature agentless is truthy', () => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: true, + } as any); + + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: true, + isCloudEnabled: false, + }, + }); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeTruthy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeTruthy(); + }); + + it('should return isAgentlessEnabled as falsy and isDefaultAgentlessPolicyEnabled as falsy with isServerlessEnabled and experimental feature agentless is falsy', () => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: true, + isCloudEnabled: false, + }, + }); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeFalsy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/fleet/public/hooks/use_agentless.ts b/x-pack/plugins/fleet/public/hooks/use_agentless.ts new file mode 100644 index 0000000000000..dba525687efc8 --- /dev/null +++ b/x-pack/plugins/fleet/public/hooks/use_agentless.ts @@ -0,0 +1,56 @@ +/* + * 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 { ExperimentalFeaturesService } from '../services'; +import type { AgentPolicy, NewPackagePolicy, PackageInfo } from '../types'; + +import { AGENTLESS_POLICY_ID } from '../../common/constants'; +import { isAgentlessIntegration as isAgentlessIntegrationFn } from '../../common/services/agentless_helper'; + +import { useConfig } from './use_config'; +import { useStartServices } from './use_core'; + +export const useAgentless = () => { + const config = useConfig(); + const { agentless: agentlessExperimentalFeatureEnabled } = ExperimentalFeaturesService.get(); + const { cloud } = useStartServices(); + const isServerless = !!cloud?.isServerlessEnabled; + const isCloud = !!cloud?.isCloudEnabled; + + const isAgentlessApiEnabled = (isCloud || isServerless) && config.agentless?.enabled; + const isDefaultAgentlessPolicyEnabled = + !isAgentlessApiEnabled && isServerless && agentlessExperimentalFeatureEnabled; + + const isAgentlessEnabled = isAgentlessApiEnabled || isDefaultAgentlessPolicyEnabled; + + const isAgentlessAgentPolicy = (agentPolicy: AgentPolicy | undefined) => { + if (!agentPolicy) return false; + return ( + isAgentlessEnabled && + (agentPolicy?.id === AGENTLESS_POLICY_ID || !!agentPolicy?.supports_agentless) + ); + }; + + // When an integration has at least a policy template enabled for agentless + const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => { + return isAgentlessEnabled && isAgentlessIntegrationFn(packageInfo); + }; + + // TODO: remove this check when CSPM implements the above flag and rely only on `isAgentlessIntegration` + const isAgentlessPackagePolicy = (packagePolicy: NewPackagePolicy) => { + return isAgentlessEnabled && packagePolicy.policy_ids.includes(AGENTLESS_POLICY_ID); + }; + + return { + isAgentlessApiEnabled, + isDefaultAgentlessPolicyEnabled, + isAgentlessEnabled, + isAgentlessAgentPolicy, + isAgentlessIntegration, + isAgentlessPackagePolicy, + }; +}; diff --git a/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts b/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts index 518d8fc3f74c8..003762e90d402 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts @@ -13,7 +13,7 @@ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { isAgentlessApiEnabled } from '../../../services/utils/agentless'; -import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../common/services/agentless_policy_helper'; +import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../common/services/agentless_helper'; import type { CreatePackagePolicyRequestSchema, From 91bec0a55ed2d7769700829be4852fcca68de4eb Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 1 Oct 2024 17:16:31 -0700 Subject: [PATCH 02/20] Fix setup technology tests --- .../hooks/setup_technology.test.ts | 142 +++++++++--------- 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts index 260a5a80ed60a..ad300d6c1277b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts @@ -6,25 +6,26 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; - import { waitFor } from '@testing-library/react'; import { createPackagePolicyMock } from '../../../../../../../../common/mocks'; +import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; import { SetupTechnology } from '../../../../../../../../common/types'; -import { ExperimentalFeaturesService } from '../../../../../services'; -import { sendGetOneAgentPolicy, useStartServices, useConfig } from '../../../../../hooks'; +import { sendGetOneAgentPolicy } from '../../../../../hooks'; +import { useAgentless } from '../../../../../../../hooks'; import { SelectedPolicyTab } from '../../components'; -import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; import { useSetupTechnology } from './setup_technology'; -jest.mock('../../../../../services'); +jest.mock('../../../../../../../services'); jest.mock('../../../../../hooks', () => ({ ...jest.requireActual('../../../../../hooks'), sendGetOneAgentPolicy: jest.fn(), - useStartServices: jest.fn(), - useConfig: jest.fn(), +})); +jest.mock('../../../../../../../hooks', () => ({ + ...jest.requireActual('../../../../../../../hooks'), + useAgentless: jest.fn(), })); jest.mock('../../../../../../../../common/services/generate_new_agent_policy'); @@ -43,26 +44,12 @@ describe('useSetupTechnology', () => { }; const packagePolicyMock = createPackagePolicyMock(); - const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService); - beforeEach(() => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: true, - } as any); - (useConfig as MockFn).mockReturnValue({ - agentless: undefined, - } as any); (sendGetOneAgentPolicy as MockFn).mockResolvedValue({ data: { item: { id: 'agentless-policy-id' }, }, }); - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: true, - }, - }); - (generateNewAgentPolicyWithDefaults as MockFn).mockReturnValue({ name: 'Agentless policy for endpoint-1', supports_agentless: true, @@ -71,9 +58,11 @@ describe('useSetupTechnology', () => { }); it('should initialize with default values when agentless is disabled', () => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: false, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: false, + }); const { result } = renderHook(() => useSetupTechnology({ @@ -90,6 +79,12 @@ describe('useSetupTechnology', () => { }); it('should fetch agentless policy if agentless feature is enabled and isServerless is true', async () => { + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: true, + }); + const { waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -106,18 +101,10 @@ describe('useSetupTechnology', () => { }); it('should set agentless setup technology if agent policy supports agentless in edit page', async () => { - (useConfig as MockFn).mockReturnValue({ - agentless: { - enabled: true, - api: { - url: 'https://agentless.api.url', - }, - }, - } as any); - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isCloudEnabled: true, - }, + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: true, + isDefaultAgentlessPolicyEnabled: false, }); const { result } = renderHook(() => useSetupTechnology({ @@ -135,19 +122,12 @@ describe('useSetupTechnology', () => { }); it('should create agentless policy if agentless feature is enabled and isCloud is true and agentless.api.url', async () => { - (useConfig as MockFn).mockReturnValue({ - agentless: { - enabled: true, - api: { - url: 'https://agentless.api.url', - }, - }, - } as any); - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isCloudEnabled: true, - }, + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: true, + isDefaultAgentlessPolicyEnabled: false, }); + const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -173,19 +153,12 @@ describe('useSetupTechnology', () => { }); it('should update agentless policy name to match integration name if agentless is enabled', async () => { - (useConfig as MockFn).mockReturnValue({ - agentless: { - enabled: true, - api: { - url: 'https://agentless.api.url', - }, - }, - } as any); - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isCloudEnabled: true, - }, + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: true, + isDefaultAgentlessPolicyEnabled: false, }); + const { result, rerender } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -228,11 +201,10 @@ describe('useSetupTechnology', () => { }); it('should not create agentless policy if agentless feature is enabled and isCloud is true and agentless.api.url is not defined', async () => { - (useConfig as MockFn).mockReturnValue({} as any); - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isCloudEnabled: true, - }, + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: false, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: false, }); const { result, waitForNextUpdate } = renderHook(() => @@ -256,10 +228,10 @@ describe('useSetupTechnology', () => { }); it('should not fetch agentless policy if agentless is enabled but serverless is disabled', async () => { - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: false, - }, + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: false, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: false, }); const { result } = renderHook(() => @@ -277,6 +249,12 @@ describe('useSetupTechnology', () => { }); it('should update agent policy and selected policy tab when setup technology is agentless', async () => { + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: true, + }); + const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -298,6 +276,12 @@ describe('useSetupTechnology', () => { }); it('should update new agent policy and selected policy tab when setup technology is agent-based', async () => { + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: true, + }); + const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -329,9 +313,11 @@ describe('useSetupTechnology', () => { }); it('should not update agent policy and selected policy tab when agentless is disabled', async () => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: false, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: false, + }); const { result } = renderHook(() => useSetupTechnology({ @@ -353,6 +339,12 @@ describe('useSetupTechnology', () => { }); it('should not update agent policy and selected policy tab when setup technology matches the current one ', async () => { + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: true, + }); + const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -378,6 +370,12 @@ describe('useSetupTechnology', () => { }); it('should revert the agent policy name to the original value when switching from agentless back to agent-based', async () => { + (useAgentless as MockFn).mockReturnValue({ + isAgentlessEnabled: true, + isAgentlessApiEnabled: false, + isDefaultAgentlessPolicyEnabled: true, + }); + const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, From 41702bec707885788bb7c4021386b3fa550a43ab Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 3 Oct 2024 14:57:02 -0700 Subject: [PATCH 03/20] Remove agentless only packages and integrations from browse page if agentless deployment mode is not available --- .../common/services/agentless_helper.test.ts | 275 ++++++++++++++++++ .../fleet/common/services/agentless_helper.ts | 31 +- .../home/hooks/use_available_packages.tsx | 33 ++- .../fleet/public/hooks/use_agentless.ts | 8 +- 4 files changed, 339 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/fleet/common/services/agentless_helper.test.ts diff --git a/x-pack/plugins/fleet/common/services/agentless_helper.test.ts b/x-pack/plugins/fleet/common/services/agentless_helper.test.ts new file mode 100644 index 0000000000000..d9d40ca4e7250 --- /dev/null +++ b/x-pack/plugins/fleet/common/services/agentless_helper.test.ts @@ -0,0 +1,275 @@ +/* + * 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 type { RegistryPolicyTemplate } from '../types'; + +import { + isAgentlessIntegration, + getAgentlessAgentPolicyNameFromPackagePolicyName, + isOnlyAgentlessIntegration, + isOnlyAgentlessPolicyTemplate, +} from './agentless_helper'; + +describe('agentless_helper', () => { + describe('isAgentlessIntegration', () => { + it('should return true if packageInfo is defined and has at least one agentless integration', () => { + const packageInfo = { + policy_templates: [ + { + name: 'template1', + title: 'Template 1', + description: '', + deployment_modes: { + default: { + enabled: true, + }, + agentless: { + enabled: true, + }, + }, + }, + { + name: 'template2', + title: 'Template 2', + description: '', + deployment_modes: { + default: { + enabled: true, + }, + }, + }, + ] as RegistryPolicyTemplate[], + }; + + const result = isAgentlessIntegration(packageInfo); + + expect(result).toBe(true); + }); + + it('should return false if packageInfo is defined but does not have agentless integrations', () => { + const packageInfo = { + policy_templates: [ + { + name: 'template1', + title: 'Template 1', + description: '', + deployment_modes: { + default: { + enabled: true, + }, + agentless: { + enabled: false, + }, + }, + }, + { + name: 'template2', + title: 'Template 2', + description: '', + deployment_modes: { + default: { + enabled: false, + }, + agentless: { + enabled: false, + }, + }, + }, + ] as RegistryPolicyTemplate[], + }; + + const result = isAgentlessIntegration(packageInfo); + + expect(result).toBe(false); + }); + + it('should return false if packageInfo has no policy templates', () => { + const packageInfo = { + policy_templates: [], + }; + + const result = isAgentlessIntegration(packageInfo); + + expect(result).toBe(false); + }); + + it('should return false if packageInfo is undefined', () => { + const packageInfo = undefined; + + const result = isAgentlessIntegration(packageInfo); + + expect(result).toBe(false); + }); + }); + + describe('getAgentlessAgentPolicyNameFromPackagePolicyName', () => { + it('should return the agentless agent policy name based on the package policy name', () => { + const packagePolicyName = 'example-package-policy'; + + const result = getAgentlessAgentPolicyNameFromPackagePolicyName(packagePolicyName); + + expect(result).toBe('Agentless policy for example-package-policy'); + }); + }); + + describe('isOnlyAgentlessIntegration', () => { + it('should return true if packageInfo is defined and has only agentless integration', () => { + const packageInfo = { + policy_templates: [ + { + name: 'template1', + title: 'Template 1', + description: '', + deployment_modes: { + default: { + enabled: false, + }, + agentless: { + enabled: true, + }, + }, + }, + { + name: 'template2', + title: 'Template 2', + description: '', + deployment_modes: { + agentless: { + enabled: true, + }, + }, + }, + ] as RegistryPolicyTemplate[], + }; + + const result = isOnlyAgentlessIntegration(packageInfo); + + expect(result).toBe(true); + }); + + it('should return false if packageInfo is defined but has other deployment types', () => { + const packageInfo = { + policy_templates: [ + { + name: 'template1', + title: 'Template 1', + description: '', + deployment_modes: { + default: { + enabled: true, + }, + agentless: { + enabled: true, + }, + }, + }, + { + name: 'template2', + title: 'Template 2', + description: '', + deployment_modes: { + default: { + enabled: true, + }, + }, + }, + ] as RegistryPolicyTemplate[], + }; + + const result = isOnlyAgentlessIntegration(packageInfo); + + expect(result).toBe(false); + }); + + it('should return false if packageInfo has no policy templates', () => { + const packageInfo = { + policy_templates: [], + }; + + const result = isOnlyAgentlessIntegration(packageInfo); + + expect(result).toBe(false); + }); + + it('should return false if packageInfo is undefined', () => { + const packageInfo = undefined; + + const result = isOnlyAgentlessIntegration(packageInfo); + + expect(result).toBe(false); + }); + }); + + describe('isOnlyAgentlessPolicyTemplate', () => { + it('should return true if the policy template is only agentless', () => { + const policyTemplate = { + name: 'template1', + title: 'Template 1', + description: '', + deployment_modes: { + default: { + enabled: false, + }, + agentless: { + enabled: true, + }, + }, + }; + const policyTemplate2 = { + name: 'template2', + title: 'Template 2', + description: '', + deployment_modes: { + agentless: { + enabled: true, + }, + }, + }; + + const result = isOnlyAgentlessPolicyTemplate(policyTemplate); + const result2 = isOnlyAgentlessPolicyTemplate(policyTemplate2); + + expect(result).toBe(true); + expect(result2).toBe(true); + }); + + it('should return false if the policy template has other deployment types', () => { + const policyTemplate = { + name: 'template1', + title: 'Template 1', + description: '', + deployment_modes: { + default: { + enabled: true, + }, + agentless: { + enabled: true, + }, + }, + }; + const policyTemplate2 = { + name: 'template2', + title: 'Template 2', + description: '', + deployment_modes: { + default: { + enabled: true, + }, + agentless: { + enabled: false, + }, + }, + }; + + const result = isOnlyAgentlessPolicyTemplate(policyTemplate); + const result2 = isOnlyAgentlessPolicyTemplate(policyTemplate2); + + expect(result).toBe(false); + expect(result2).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/fleet/common/services/agentless_helper.ts b/x-pack/plugins/fleet/common/services/agentless_helper.ts index 60796f0719890..adca5ef8c93cf 100644 --- a/x-pack/plugins/fleet/common/services/agentless_helper.ts +++ b/x-pack/plugins/fleet/common/services/agentless_helper.ts @@ -5,9 +5,11 @@ * 2.0. */ -import type { PackageInfo } from '../types'; +import type { PackageInfo, RegistryPolicyTemplate } from '../types'; -export const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => { +export const isAgentlessIntegration = ( + packageInfo: Pick | undefined +) => { if ( packageInfo?.policy_templates && packageInfo?.policy_templates.length > 0 && @@ -23,3 +25,28 @@ export const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => export const getAgentlessAgentPolicyNameFromPackagePolicyName = (packagePolicyName: string) => { return `Agentless policy for ${packagePolicyName}`; }; + +export const isOnlyAgentlessIntegration = ( + packageInfo: Pick | undefined +) => { + if ( + packageInfo?.policy_templates && + packageInfo?.policy_templates.length > 0 && + packageInfo?.policy_templates.every((policyTemplate) => + isOnlyAgentlessPolicyTemplate(policyTemplate) + ) + ) { + return true; + } + return false; +}; + +export const isOnlyAgentlessPolicyTemplate = (policyTemplate: { + deployment_modes?: RegistryPolicyTemplate['deployment_modes']; +}) => { + return ( + policyTemplate.deployment_modes?.agentless.enabled === true && + (!policyTemplate.deployment_modes?.default || + policyTemplate.deployment_modes?.default.enabled === false) + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx index c7b1f936e2424..b40de8fabfb43 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx @@ -11,8 +11,10 @@ import { uniq } from 'lodash'; import type { CustomIntegration } from '@kbn/custom-integrations-plugin/common'; import type { IntegrationPreferenceType } from '../../../components/integration_preference'; -import { useGetPackagesQuery, useGetCategoriesQuery } from '../../../../../hooks'; +import { useAgentless } from '../../../../../../../hooks'; import { + useGetPackagesQuery, + useGetCategoriesQuery, useGetAppendCustomIntegrationsQuery, useGetReplacementCustomIntegrationsQuery, } from '../../../../../hooks'; @@ -113,6 +115,8 @@ export const useAvailablePackages = ({ const [preference, setPreference] = useState('recommended'); const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get(); + const { isAgentlessEnabled, isOnlyAgentlessIntegration, isOnlyAgentlessPolicyTemplate } = + useAgentless(); const { initialSelectedCategory, @@ -146,10 +150,29 @@ export const useAvailablePackages = ({ }); } - const eprIntegrationList = useMemo( - () => packageListToIntegrationsList(eprPackages?.items || []), - [eprPackages] - ); + const eprIntegrationList = useMemo(() => { + // Filter out agentless only packages and policy templates if agentless is not available + const packages = isAgentlessEnabled + ? eprPackages?.items + : eprPackages?.items + .filter((pkg) => { + return !isOnlyAgentlessIntegration(pkg); + }) + .forEach((pkg) => { + pkg.policy_templates = (pkg.policy_templates || []).filter((policyTemplate) => { + return !isOnlyAgentlessPolicyTemplate(policyTemplate); + }); + }); + + const integrations = packageListToIntegrationsList(packages || []); + return integrations; + }, [ + eprPackages?.items, + isAgentlessEnabled, + isOnlyAgentlessIntegration, + isOnlyAgentlessPolicyTemplate, + ]); + const { data: replacementCustomIntegrations, isInitialLoading: isLoadingReplacmentCustomIntegrations, diff --git a/x-pack/plugins/fleet/public/hooks/use_agentless.ts b/x-pack/plugins/fleet/public/hooks/use_agentless.ts index dba525687efc8..e9f5376f7cfb6 100644 --- a/x-pack/plugins/fleet/public/hooks/use_agentless.ts +++ b/x-pack/plugins/fleet/public/hooks/use_agentless.ts @@ -9,7 +9,11 @@ import { ExperimentalFeaturesService } from '../services'; import type { AgentPolicy, NewPackagePolicy, PackageInfo } from '../types'; import { AGENTLESS_POLICY_ID } from '../../common/constants'; -import { isAgentlessIntegration as isAgentlessIntegrationFn } from '../../common/services/agentless_helper'; +import { + isAgentlessIntegration as isAgentlessIntegrationFn, + isOnlyAgentlessPolicyTemplate, + isOnlyAgentlessIntegration, +} from '../../common/services/agentless_helper'; import { useConfig } from './use_config'; import { useStartServices } from './use_core'; @@ -52,5 +56,7 @@ export const useAgentless = () => { isAgentlessAgentPolicy, isAgentlessIntegration, isAgentlessPackagePolicy, + isOnlyAgentlessIntegration, + isOnlyAgentlessPolicyTemplate, }; }; From 9e0dd25e1d2428defd65bea4a270593af085c585 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 3 Oct 2024 16:09:32 -0700 Subject: [PATCH 04/20] Prevent agentless integrations from being installed when agentless is not available --- x-pack/plugins/fleet/server/errors/handlers.ts | 4 ++++ x-pack/plugins/fleet/server/errors/index.ts | 1 + .../server/services/epm/packages/install.ts | 16 ++++++++++++++++ .../fleet/server/services/utils/agentless.ts | 1 + 4 files changed, 22 insertions(+) diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts index d8971948397d3..8d24be8345571 100644 --- a/x-pack/plugins/fleet/server/errors/handlers.ts +++ b/x-pack/plugins/fleet/server/errors/handlers.ts @@ -45,6 +45,7 @@ import { PackageSavedObjectConflictError, FleetTooManyRequestsError, AgentlessPolicyExistsRequestError, + PackageInvalidDeploymentMode, } from '.'; type IngestErrorHandler = ( @@ -60,6 +61,9 @@ interface IngestErrorHandlerParams { // this type is based on BadRequest values observed while debugging https://github.com/elastic/kibana/issues/75862 const getHTTPResponseCode = (error: FleetError): number => { // Bad Request + if (error instanceof PackageInvalidDeploymentMode) { + return 400; + } if (error instanceof PackageFailedVerificationError) { return 400; } diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index 09b387e7a5cee..5eb7f87b939cf 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -29,6 +29,7 @@ export class RegistryResponseError extends RegistryError { // Package errors +export class PackageInvalidDeploymentMode extends FleetError {} export class PackageOutdatedError extends FleetError {} export class PackageFailedVerificationError extends FleetError { constructor(pkgName: string, pkgVersion: string) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 65f1a75f76f84..ce472eb94e528 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -60,6 +60,7 @@ import { FleetUnauthorizedError, PackageNotFoundError, FleetTooManyRequestsError, + PackageInvalidDeploymentMode, } from '../../../errors'; import { PACKAGES_SAVED_OBJECT_TYPE, @@ -82,6 +83,8 @@ import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; import { auditLoggingService } from '../../audit_logging'; import { getFilteredInstallPackages } from '../filtered_packages'; +import { isAgentlessEnabled, isAgentlessIntegration } from '../../utils/agentless'; + import { _stateMachineInstallPackage } from './install_state_machine/_state_machine_package_install'; import { formatVerificationResultForSO } from './package_verification'; @@ -507,6 +510,19 @@ async function installPackageFromRegistry({ }` ); } + + // only allow install of agentless packages if agentless is enabled, or if using force flag + if (!isAgentlessEnabled() && isAgentlessIntegration(packageInfo)) { + if (!force) { + throw new PackageInvalidDeploymentMode( + `${pkgkey} contains agentless policy templates, agentless is not available on this deployment` + ); + } + logger.debug( + `${pkgkey} contains agentless policy templates, agentless is not available on this deployment but installing anyway due to force flag` + ); + } + return await installPackageWithStateMachine({ pkgName, pkgVersion, diff --git a/x-pack/plugins/fleet/server/services/utils/agentless.ts b/x-pack/plugins/fleet/server/services/utils/agentless.ts index c85e9cc991a6c..0edbb72c73470 100644 --- a/x-pack/plugins/fleet/server/services/utils/agentless.ts +++ b/x-pack/plugins/fleet/server/services/utils/agentless.ts @@ -7,6 +7,7 @@ import { appContextService } from '..'; import type { FleetConfigType } from '../../config'; +export { isAgentlessIntegration } from '../../../common/services/agentless_helper'; export const isAgentlessApiEnabled = () => { const cloudSetup = appContextService.getCloud(); From 0b7b867f31eb372855b1e3a6ca015f88eef45d5d Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 7 Oct 2024 13:01:11 -0700 Subject: [PATCH 05/20] Fix tests by adding condition to getCloud --- x-pack/plugins/fleet/server/services/utils/agentless.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/utils/agentless.ts b/x-pack/plugins/fleet/server/services/utils/agentless.ts index 0edbb72c73470..294ea6a13a7af 100644 --- a/x-pack/plugins/fleet/server/services/utils/agentless.ts +++ b/x-pack/plugins/fleet/server/services/utils/agentless.ts @@ -10,12 +10,12 @@ import type { FleetConfigType } from '../../config'; export { isAgentlessIntegration } from '../../../common/services/agentless_helper'; export const isAgentlessApiEnabled = () => { - const cloudSetup = appContextService.getCloud(); + const cloudSetup = appContextService.getCloud && appContextService.getCloud(); const isHosted = cloudSetup?.isCloudEnabled || cloudSetup?.isServerlessEnabled; return Boolean(isHosted && appContextService.getConfig()?.agentless?.enabled); }; export const isDefaultAgentlessPolicyEnabled = () => { - const cloudSetup = appContextService.getCloud(); + const cloudSetup = appContextService.getCloud && appContextService.getCloud(); return Boolean( cloudSetup?.isServerlessEnabled && appContextService.getExperimentalFeatures().agentless ); @@ -45,7 +45,7 @@ export const prependAgentlessApiBasePathToEndpoint = ( agentlessConfig: FleetConfigType['agentless'], endpoint: AgentlessApiEndpoints ) => { - const cloudSetup = appContextService.getCloud(); + const cloudSetup = appContextService.getCloud && appContextService.getCloud(); const endpointPrefix = cloudSetup?.isServerlessEnabled ? AGENTLESS_SERVERLESS_API_BASE_PATH : AGENTLESS_ESS_API_BASE_PATH; From 894674e95f982b4a708d0bf08152cfea1e3efd5c Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 7 Oct 2024 13:11:57 -0700 Subject: [PATCH 06/20] Move filtering of deployment mode to separate fn --- .../home/hooks/use_available_packages.tsx | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx index b40de8fabfb43..98e36eb1d8522 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx @@ -30,6 +30,11 @@ import { isIntegrationPolicyTemplate, } from '../../../../../../../../common/services'; +import { + isOnlyAgentlessPolicyTemplate, + isOnlyAgentlessIntegration, +} from '../../../../../../../../common/services/agentless_helper'; + import type { IntegrationCardItem } from '..'; import { ALL_CATEGORY } from '../category_facets'; @@ -105,6 +110,22 @@ const packageListToIntegrationsList = (packages: PackageList): PackageList => { }, []); }; +// Return filtered packages based on deployment mode, +// Currently filters out agentless only packages and policy templates if agentless is not available +const filterPackageListDeploymentModes = (packages: PackageList, isAgentlessEnabled: boolean) => { + return isAgentlessEnabled + ? packages + : packages + .filter((pkg) => { + return !isOnlyAgentlessIntegration(pkg); + }) + .forEach((pkg) => { + pkg.policy_templates = (pkg.policy_templates || []).filter((policyTemplate) => { + return !isOnlyAgentlessPolicyTemplate(policyTemplate); + }); + }); +}; + export type AvailablePackagesHookType = typeof useAvailablePackages; export const useAvailablePackages = ({ @@ -115,8 +136,7 @@ export const useAvailablePackages = ({ const [preference, setPreference] = useState('recommended'); const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get(); - const { isAgentlessEnabled, isOnlyAgentlessIntegration, isOnlyAgentlessPolicyTemplate } = - useAgentless(); + const { isAgentlessEnabled } = useAgentless(); const { initialSelectedCategory, @@ -151,27 +171,11 @@ export const useAvailablePackages = ({ } const eprIntegrationList = useMemo(() => { - // Filter out agentless only packages and policy templates if agentless is not available - const packages = isAgentlessEnabled - ? eprPackages?.items - : eprPackages?.items - .filter((pkg) => { - return !isOnlyAgentlessIntegration(pkg); - }) - .forEach((pkg) => { - pkg.policy_templates = (pkg.policy_templates || []).filter((policyTemplate) => { - return !isOnlyAgentlessPolicyTemplate(policyTemplate); - }); - }); - - const integrations = packageListToIntegrationsList(packages || []); + const filteredPackageList = + filterPackageListDeploymentModes(eprPackages?.items || [], isAgentlessEnabled) || []; + const integrations = packageListToIntegrationsList(filteredPackageList); return integrations; - }, [ - eprPackages?.items, - isAgentlessEnabled, - isOnlyAgentlessIntegration, - isOnlyAgentlessPolicyTemplate, - ]); + }, [eprPackages?.items, isAgentlessEnabled]); const { data: replacementCustomIntegrations, From f8962936ca98d63a34cff26841f9dae9e6f19f87 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 8 Oct 2024 13:19:35 -0700 Subject: [PATCH 07/20] Try to fix tests --- .../steps/components/package_policy_input_panel.test.tsx | 8 ++++++-- .../hooks/use_package_policy_steps.tsx | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx index 5accdf37e95e7..c4cfa4f62e922 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx @@ -10,7 +10,7 @@ import { waitFor } from '@testing-library/react'; import { createFleetTestRendererMock } from '../../../../../../../../mock'; import type { TestRenderer } from '../../../../../../../../mock'; -import { useAgentless } from '../../../single_page_layout/hooks/setup_technology'; +import { useAgentless } from '../../../../../../../../hooks'; import type { PackageInfo, @@ -21,7 +21,7 @@ import type { import { shouldShowStreamsByDefault, PackagePolicyInputPanel } from './package_policy_input_panel'; -jest.mock('../../../single_page_layout/hooks/setup_technology', () => { +jest.mock('../../../../../../../../hooks', () => { return { useAgentless: jest.fn(), }; @@ -365,6 +365,8 @@ describe('PackagePolicyInputPanel', () => { isAgentlessIntegration: jest.fn(), isAgentlessApiEnabled: true, isDefaultAgentlessPolicyEnabled: false, + isOnlyAgentlessIntegration: jest.fn(), + isOnlyAgentlessPolicyTemplate: jest.fn(), }); }); @@ -400,6 +402,8 @@ describe('PackagePolicyInputPanel', () => { isAgentlessIntegration: jest.fn(), isAgentlessApiEnabled: true, isDefaultAgentlessPolicyEnabled: false, + isOnlyAgentlessIntegration: jest.fn(), + isOnlyAgentlessPolicyTemplate: jest.fn(), }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx index dc055cec7fceb..1f2bdecf9e5ad 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx @@ -135,7 +135,6 @@ export function usePackagePolicySteps({ setNewAgentPolicy, updateAgentPolicies, setSelectedPolicyTab, - packageInfo, packagePolicy, isEditPage: true, agentPolicies, From 5f058fb0e8b4616c8211f79b9904e3370cece9e2 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 11 Oct 2024 13:11:05 -0700 Subject: [PATCH 08/20] =?UTF-8?q?Fix=20integrations=20list=20not=20being?= =?UTF-8?q?=20returned=20=F0=9F=A4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fleet/common/services/agentless_helper.test.ts | 12 ++++++++++++ .../fleet/common/services/agentless_helper.ts | 11 +++++------ .../screens/home/hooks/use_available_packages.tsx | 3 ++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/fleet/common/services/agentless_helper.test.ts b/x-pack/plugins/fleet/common/services/agentless_helper.test.ts index d9d40ca4e7250..0ed5926e75506 100644 --- a/x-pack/plugins/fleet/common/services/agentless_helper.test.ts +++ b/x-pack/plugins/fleet/common/services/agentless_helper.test.ts @@ -271,5 +271,17 @@ describe('agentless_helper', () => { expect(result).toBe(false); expect(result2).toBe(false); }); + + it('should return false if the policy template has no deployment modes', () => { + const policyTemplate = { + name: 'template1', + title: 'Template 1', + description: '', + }; + + const result = isOnlyAgentlessPolicyTemplate(policyTemplate); + + expect(result).toBe(false); + }); }); }); diff --git a/x-pack/plugins/fleet/common/services/agentless_helper.ts b/x-pack/plugins/fleet/common/services/agentless_helper.ts index adca5ef8c93cf..4a277251d7acd 100644 --- a/x-pack/plugins/fleet/common/services/agentless_helper.ts +++ b/x-pack/plugins/fleet/common/services/agentless_helper.ts @@ -41,12 +41,11 @@ export const isOnlyAgentlessIntegration = ( return false; }; -export const isOnlyAgentlessPolicyTemplate = (policyTemplate: { - deployment_modes?: RegistryPolicyTemplate['deployment_modes']; -}) => { +export const isOnlyAgentlessPolicyTemplate = (policyTemplate: RegistryPolicyTemplate) => { return ( - policyTemplate.deployment_modes?.agentless.enabled === true && - (!policyTemplate.deployment_modes?.default || - policyTemplate.deployment_modes?.default.enabled === false) + policyTemplate.deployment_modes && + policyTemplate.deployment_modes.agentless.enabled === true && + (!policyTemplate.deployment_modes.default || + policyTemplate.deployment_modes.default.enabled === false) ); }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx index 98e36eb1d8522..f5e4475f58b3d 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx @@ -119,10 +119,11 @@ const filterPackageListDeploymentModes = (packages: PackageList, isAgentlessEnab .filter((pkg) => { return !isOnlyAgentlessIntegration(pkg); }) - .forEach((pkg) => { + .map((pkg) => { pkg.policy_templates = (pkg.policy_templates || []).filter((policyTemplate) => { return !isOnlyAgentlessPolicyTemplate(policyTemplate); }); + return pkg; }); }; From c036bfeb30271a11f12b51e594b3b4b72822471f Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 11 Oct 2024 14:59:32 -0700 Subject: [PATCH 09/20] Fix return --- x-pack/plugins/fleet/common/services/agentless_helper.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/common/services/agentless_helper.ts b/x-pack/plugins/fleet/common/services/agentless_helper.ts index 4a277251d7acd..7093875ae84f5 100644 --- a/x-pack/plugins/fleet/common/services/agentless_helper.ts +++ b/x-pack/plugins/fleet/common/services/agentless_helper.ts @@ -42,10 +42,10 @@ export const isOnlyAgentlessIntegration = ( }; export const isOnlyAgentlessPolicyTemplate = (policyTemplate: RegistryPolicyTemplate) => { - return ( + return Boolean( policyTemplate.deployment_modes && - policyTemplate.deployment_modes.agentless.enabled === true && - (!policyTemplate.deployment_modes.default || - policyTemplate.deployment_modes.default.enabled === false) + policyTemplate.deployment_modes.agentless.enabled === true && + (!policyTemplate.deployment_modes.default || + policyTemplate.deployment_modes.default.enabled === false) ); }; From 7e922ad72a4900259ad96c316847ba507ff99747 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 11 Oct 2024 15:01:34 -0700 Subject: [PATCH 10/20] Revert "Move useAgentless to top-level hook" This reverts commit 3412a60c65ea85b9fe8017ec78656ab168c9e0d9. --- ...s_helper.ts => agentless_policy_helper.ts} | 0 .../components/package_policy_input_panel.tsx | 2 +- .../single_page_layout/components/layout.tsx | 3 +- .../single_page_layout/hooks/form.tsx | 3 +- .../hooks/setup_technology.test.ts | 107 ++++++++++++++- .../hooks/setup_technology.ts | 68 +++++++++- .../single_page_layout/index.tsx | 3 +- .../edit_package_policy_page/index.tsx | 3 +- x-pack/plugins/fleet/public/hooks/index.ts | 1 - .../fleet/public/hooks/use_agentless.test.ts | 127 ------------------ .../fleet/public/hooks/use_agentless.ts | 62 --------- .../routes/package_policy/utils/index.ts | 2 +- 12 files changed, 178 insertions(+), 203 deletions(-) rename x-pack/plugins/fleet/common/services/{agentless_helper.ts => agentless_policy_helper.ts} (100%) delete mode 100644 x-pack/plugins/fleet/public/hooks/use_agentless.test.ts delete mode 100644 x-pack/plugins/fleet/public/hooks/use_agentless.ts diff --git a/x-pack/plugins/fleet/common/services/agentless_helper.ts b/x-pack/plugins/fleet/common/services/agentless_policy_helper.ts similarity index 100% rename from x-pack/plugins/fleet/common/services/agentless_helper.ts rename to x-pack/plugins/fleet/common/services/agentless_policy_helper.ts diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx index 66e115c06ec2f..0661c2f36a9b3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx @@ -31,7 +31,7 @@ import type { } from '../../../../../../types'; import type { PackagePolicyInputValidationResults } from '../../../services'; import { hasInvalidButRequiredVar, countValidationErrors } from '../../../services'; -import { useAgentless } from '../../../../../../hooks'; +import { useAgentless } from '../../../single_page_layout/hooks/setup_technology'; import { PackagePolicyInputConfig } from './package_policy_input_config'; import { PackagePolicyInputStreamConfig } from './package_policy_input_stream'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx index 910c27e8df66d..c5170d33eee68 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx @@ -19,7 +19,8 @@ import { EuiSpacer, } from '@elastic/eui'; -import { useAgentless } from '../../../../../hooks'; +import { useAgentless } from '../hooks/setup_technology'; + import { WithHeaderLayout } from '../../../../../layouts'; import type { AgentPolicy, PackageInfo, RegistryPolicyTemplate } from '../../../../../types'; import { PackageIcon } from '../../../../../components'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 0f2c230233fdd..2bae962f48e7c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -27,7 +27,6 @@ import { sendBulkInstallPackages, sendGetPackagePolicies, useMultipleAgentPolicies, - useAgentless, } from '../../../../../hooks'; import { isVerificationError, packageToPackagePolicy } from '../../../../../services'; import { @@ -50,6 +49,8 @@ import { getCloudShellUrlFromPackagePolicy, } from '../../../../../../../components/cloud_security_posture/services'; +import { useAgentless } from './setup_technology'; + export async function createAgentPolicy({ packagePolicy, newAgentPolicy, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts index ad300d6c1277b..b0aacdb4abc0c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts @@ -16,7 +16,7 @@ import { sendGetOneAgentPolicy } from '../../../../../hooks'; import { useAgentless } from '../../../../../../../hooks'; import { SelectedPolicyTab } from '../../components'; -import { useSetupTechnology } from './setup_technology'; +import { useAgentless, useSetupTechnology } from './setup_technology'; jest.mock('../../../../../../../services'); jest.mock('../../../../../hooks', () => ({ @@ -31,7 +31,110 @@ jest.mock('../../../../../../../../common/services/generate_new_agent_policy'); type MockFn = jest.MockedFunction; -// TODO: Fix mocks +describe('useAgentless', () => { + const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService); + + beforeEach(() => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + (useConfig as MockFn).mockReturnValue({ + agentless: undefined, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: false, + isCloudEnabled: false, + }, + }); + jest.clearAllMocks(); + }); + + it('should not return isAgentless when agentless is not enabled', () => { + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeFalsy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); + + it('should return isAgentlessEnabled as falsy if agentless.enabled is true and experimental feature agentless is truthy without cloud or serverless', () => { + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + }, + } as any); + + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeFalsy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); + + it('should return isAgentlessEnabled and isAgentlessApiEnabled as truthy with isCloudEnabled', () => { + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + }, + } as any); + + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: false, + isCloudEnabled: true, + }, + }); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeTruthy(); + expect(result.current.isAgentlessApiEnabled).toBeTruthy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); + it('should return isAgentlessEnabled and isDefaultAgentlessPolicyEnabled as truthy with isServerlessEnabled and experimental feature agentless is truthy', () => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: true, + } as any); + + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: true, + isCloudEnabled: false, + }, + }); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeTruthy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeTruthy(); + }); + + it('should return isAgentlessEnabled as falsy and isDefaultAgentlessPolicyEnabled as falsy with isServerlessEnabled and experimental feature agentless is falsy', () => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: true, + isCloudEnabled: false, + }, + }); + + const { result } = renderHook(() => useAgentless()); + + expect(result.current.isAgentlessEnabled).toBeFalsy(); + expect(result.current.isAgentlessApiEnabled).toBeFalsy(); + expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); + }); +}); + describe('useSetupTechnology', () => { const setNewAgentPolicy = jest.fn(); const updateAgentPoliciesMock = jest.fn(); 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 4cc7b40250712..fb6aefcf7583e 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 @@ -7,20 +7,77 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import type { AgentPolicy, NewAgentPolicy, NewPackagePolicy } from '../../../../../types'; +import { useConfig } from '../../../../../hooks'; +import { ExperimentalFeaturesService } from '../../../../../services'; +import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; +import type { + AgentPolicy, + NewAgentPolicy, + NewPackagePolicy, + PackageInfo, +} from '../../../../../types'; import { SetupTechnology } from '../../../../../types'; -import { sendGetOneAgentPolicy } from '../../../../../hooks'; +import { sendGetOneAgentPolicy, useStartServices } from '../../../../../hooks'; import { SelectedPolicyTab } from '../../components'; -import { useAgentless } from '../../../../../../../hooks'; import { AGENTLESS_POLICY_ID } from '../../../../../../../../common/constants'; -import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; -import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../../../../../common/services/agentless_helper'; +import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../../../../../common/services/agentless_policy_helper'; + +export const useAgentless = () => { + const config = useConfig(); + const { agentless: agentlessExperimentalFeatureEnabled } = ExperimentalFeaturesService.get(); + const { cloud } = useStartServices(); + const isServerless = !!cloud?.isServerlessEnabled; + const isCloud = !!cloud?.isCloudEnabled; + + const isAgentlessApiEnabled = (isCloud || isServerless) && config.agentless?.enabled; + const isDefaultAgentlessPolicyEnabled = + !isAgentlessApiEnabled && isServerless && agentlessExperimentalFeatureEnabled; + + const isAgentlessEnabled = isAgentlessApiEnabled || isDefaultAgentlessPolicyEnabled; + + const isAgentlessAgentPolicy = (agentPolicy: AgentPolicy | undefined) => { + if (!agentPolicy) return false; + return ( + isAgentlessEnabled && + (agentPolicy?.id === AGENTLESS_POLICY_ID || !!agentPolicy?.supports_agentless) + ); + }; + + // When an integration has at least a policy template enabled for agentless + const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => { + if ( + isAgentlessEnabled && + packageInfo?.policy_templates && + packageInfo?.policy_templates.length > 0 && + !!packageInfo?.policy_templates.find( + (policyTemplate) => policyTemplate?.deployment_modes?.agentless.enabled === true + ) + ) { + return true; + } + return false; + }; + + // TODO: remove this check when CSPM implements the above flag and rely only on `isAgentlessIntegration` + const isAgentlessPackagePolicy = (packagePolicy: NewPackagePolicy) => { + return isAgentlessEnabled && packagePolicy.policy_ids.includes(AGENTLESS_POLICY_ID); + }; + return { + isAgentlessApiEnabled, + isDefaultAgentlessPolicyEnabled, + isAgentlessEnabled, + isAgentlessAgentPolicy, + isAgentlessIntegration, + isAgentlessPackagePolicy, + }; +}; export function useSetupTechnology({ setNewAgentPolicy, newAgentPolicy, updateAgentPolicies, setSelectedPolicyTab, + packageInfo, packagePolicy, isEditPage, agentPolicies, @@ -29,6 +86,7 @@ export function useSetupTechnology({ newAgentPolicy: NewAgentPolicy; updateAgentPolicies: (policies: AgentPolicy[]) => void; setSelectedPolicyTab: (tab: SelectedPolicyTab) => void; + packageInfo?: PackageInfo; packagePolicy: NewPackagePolicy; isEditPage?: boolean; agentPolicies?: AgentPolicy[]; 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 f4d0367ccd0e0..24f8fa8a04fe5 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 @@ -51,7 +51,6 @@ import { useStartServices, useUIExtension, useAuthz, - useAgentless, } from '../../../../hooks'; import { DevtoolsRequestFlyoutButton, @@ -83,6 +82,7 @@ import { PostInstallCloudFormationModal } from './components/cloud_security_post import { PostInstallGoogleCloudShellModal } from './components/cloud_security_posture/post_install_google_cloud_shell_modal'; 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)` @@ -356,6 +356,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ setNewAgentPolicy, updateAgentPolicies, setSelectedPolicyTab, + packageInfo, packagePolicy, }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 4db1ff54e9dcd..6157f09968680 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -27,7 +27,6 @@ import { useUIExtension, sendGetAgentStatus, useAuthz, - useAgentless, } from '../../../hooks'; import { useBreadcrumbs as useIntegrationsBreadcrumbs, @@ -61,6 +60,8 @@ import { RootPrivilegesCallout } from '../create_package_policy_page/single_page import { StepsWithLessPadding } from '../create_package_policy_page/single_page_layout'; +import { useAgentless } from '../create_package_policy_page/single_page_layout/hooks/setup_technology'; + import { UpgradeStatusCallout } from './components'; import { usePackagePolicyWithRelatedData, useHistoryBlock } from './hooks'; import { getNewSecrets } from './utils'; diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index 6452415f2159b..f537698897a19 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -35,4 +35,3 @@ export * from './use_locator'; export * from './use_agent_version'; export * from './use_fleet_server_agents'; export * from './use_multiple_agent_policies'; -export * from './use_agentless'; diff --git a/x-pack/plugins/fleet/public/hooks/use_agentless.test.ts b/x-pack/plugins/fleet/public/hooks/use_agentless.test.ts deleted file mode 100644 index 8558389f13266..0000000000000 --- a/x-pack/plugins/fleet/public/hooks/use_agentless.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 { renderHook } from '@testing-library/react-hooks'; - -import { ExperimentalFeaturesService } from '../services'; - -import { useStartServices, useConfig } from '.'; -import { useAgentless } from './use_agentless'; - -jest.mock('../services'); -jest.mock('./use_config', () => ({ - useConfig: jest.fn(), -})); -jest.mock('./use_core', () => ({ - useStartServices: jest.fn(), -})); - -type MockFn = jest.MockedFunction; - -describe('useAgentless', () => { - const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService); - - beforeEach(() => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); - (useConfig as MockFn).mockReturnValue({ - agentless: undefined, - } as any); - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: false, - isCloudEnabled: false, - }, - }); - jest.clearAllMocks(); - }); - - it('should not return isAgentless when agentless is not enabled', () => { - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeFalsy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); - - it('should return isAgentlessEnabled as falsy if agentless.enabled is true and experimental feature agentless is truthy without cloud or serverless', () => { - (useConfig as MockFn).mockReturnValue({ - agentless: { - enabled: true, - }, - } as any); - - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeFalsy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); - - it('should return isAgentlessEnabled and isAgentlessApiEnabled as truthy with isCloudEnabled', () => { - (useConfig as MockFn).mockReturnValue({ - agentless: { - enabled: true, - }, - } as any); - - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: false, - isCloudEnabled: true, - }, - }); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeTruthy(); - expect(result.current.isAgentlessApiEnabled).toBeTruthy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); - it('should return isAgentlessEnabled and isDefaultAgentlessPolicyEnabled as truthy with isServerlessEnabled and experimental feature agentless is truthy', () => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: true, - } as any); - - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: true, - isCloudEnabled: false, - }, - }); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeTruthy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeTruthy(); - }); - - it('should return isAgentlessEnabled as falsy and isDefaultAgentlessPolicyEnabled as falsy with isServerlessEnabled and experimental feature agentless is falsy', () => { - mockedExperimentalFeaturesService.get.mockReturnValue({ - agentless: false, - } as any); - - (useStartServices as MockFn).mockReturnValue({ - cloud: { - isServerlessEnabled: true, - isCloudEnabled: false, - }, - }); - - const { result } = renderHook(() => useAgentless()); - - expect(result.current.isAgentlessEnabled).toBeFalsy(); - expect(result.current.isAgentlessApiEnabled).toBeFalsy(); - expect(result.current.isDefaultAgentlessPolicyEnabled).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/fleet/public/hooks/use_agentless.ts b/x-pack/plugins/fleet/public/hooks/use_agentless.ts deleted file mode 100644 index e9f5376f7cfb6..0000000000000 --- a/x-pack/plugins/fleet/public/hooks/use_agentless.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 { ExperimentalFeaturesService } from '../services'; -import type { AgentPolicy, NewPackagePolicy, PackageInfo } from '../types'; - -import { AGENTLESS_POLICY_ID } from '../../common/constants'; -import { - isAgentlessIntegration as isAgentlessIntegrationFn, - isOnlyAgentlessPolicyTemplate, - isOnlyAgentlessIntegration, -} from '../../common/services/agentless_helper'; - -import { useConfig } from './use_config'; -import { useStartServices } from './use_core'; - -export const useAgentless = () => { - const config = useConfig(); - const { agentless: agentlessExperimentalFeatureEnabled } = ExperimentalFeaturesService.get(); - const { cloud } = useStartServices(); - const isServerless = !!cloud?.isServerlessEnabled; - const isCloud = !!cloud?.isCloudEnabled; - - const isAgentlessApiEnabled = (isCloud || isServerless) && config.agentless?.enabled; - const isDefaultAgentlessPolicyEnabled = - !isAgentlessApiEnabled && isServerless && agentlessExperimentalFeatureEnabled; - - const isAgentlessEnabled = isAgentlessApiEnabled || isDefaultAgentlessPolicyEnabled; - - const isAgentlessAgentPolicy = (agentPolicy: AgentPolicy | undefined) => { - if (!agentPolicy) return false; - return ( - isAgentlessEnabled && - (agentPolicy?.id === AGENTLESS_POLICY_ID || !!agentPolicy?.supports_agentless) - ); - }; - - // When an integration has at least a policy template enabled for agentless - const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => { - return isAgentlessEnabled && isAgentlessIntegrationFn(packageInfo); - }; - - // TODO: remove this check when CSPM implements the above flag and rely only on `isAgentlessIntegration` - const isAgentlessPackagePolicy = (packagePolicy: NewPackagePolicy) => { - return isAgentlessEnabled && packagePolicy.policy_ids.includes(AGENTLESS_POLICY_ID); - }; - - return { - isAgentlessApiEnabled, - isDefaultAgentlessPolicyEnabled, - isAgentlessEnabled, - isAgentlessAgentPolicy, - isAgentlessIntegration, - isAgentlessPackagePolicy, - isOnlyAgentlessIntegration, - isOnlyAgentlessPolicyTemplate, - }; -}; diff --git a/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts b/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts index 003762e90d402..518d8fc3f74c8 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts @@ -13,7 +13,7 @@ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { isAgentlessApiEnabled } from '../../../services/utils/agentless'; -import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../common/services/agentless_helper'; +import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../common/services/agentless_policy_helper'; import type { CreatePackagePolicyRequestSchema, From 8aa91e0488cbfc1c24f3f8585c251b99f9180d3d Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 11 Oct 2024 15:01:45 -0700 Subject: [PATCH 11/20] Revert "Fix setup technology tests" This reverts commit 91bec0a55ed2d7769700829be4852fcca68de4eb. --- .../hooks/setup_technology.test.ts | 142 +++++++++--------- 1 file changed, 72 insertions(+), 70 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts index b0aacdb4abc0c..be7884aad753d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts @@ -6,26 +6,25 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; + import { waitFor } from '@testing-library/react'; import { createPackagePolicyMock } from '../../../../../../../../common/mocks'; -import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; import { SetupTechnology } from '../../../../../../../../common/types'; -import { sendGetOneAgentPolicy } from '../../../../../hooks'; -import { useAgentless } from '../../../../../../../hooks'; +import { ExperimentalFeaturesService } from '../../../../../services'; +import { sendGetOneAgentPolicy, useStartServices, useConfig } from '../../../../../hooks'; import { SelectedPolicyTab } from '../../components'; +import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; import { useAgentless, useSetupTechnology } from './setup_technology'; -jest.mock('../../../../../../../services'); +jest.mock('../../../../../services'); jest.mock('../../../../../hooks', () => ({ ...jest.requireActual('../../../../../hooks'), sendGetOneAgentPolicy: jest.fn(), -})); -jest.mock('../../../../../../../hooks', () => ({ - ...jest.requireActual('../../../../../../../hooks'), - useAgentless: jest.fn(), + useStartServices: jest.fn(), + useConfig: jest.fn(), })); jest.mock('../../../../../../../../common/services/generate_new_agent_policy'); @@ -147,12 +146,26 @@ describe('useSetupTechnology', () => { }; const packagePolicyMock = createPackagePolicyMock(); + const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService); + beforeEach(() => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: true, + } as any); + (useConfig as MockFn).mockReturnValue({ + agentless: undefined, + } as any); (sendGetOneAgentPolicy as MockFn).mockResolvedValue({ data: { item: { id: 'agentless-policy-id' }, }, }); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: true, + }, + }); + (generateNewAgentPolicyWithDefaults as MockFn).mockReturnValue({ name: 'Agentless policy for endpoint-1', supports_agentless: true, @@ -161,11 +174,9 @@ describe('useSetupTechnology', () => { }); it('should initialize with default values when agentless is disabled', () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: false, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: false, - }); + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); const { result } = renderHook(() => useSetupTechnology({ @@ -182,12 +193,6 @@ describe('useSetupTechnology', () => { }); it('should fetch agentless policy if agentless feature is enabled and isServerless is true', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: true, - }); - const { waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -204,10 +209,18 @@ describe('useSetupTechnology', () => { }); it('should set agentless setup technology if agent policy supports agentless in edit page', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: true, - isDefaultAgentlessPolicyEnabled: false, + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + api: { + url: 'https://agentless.api.url', + }, + }, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isCloudEnabled: true, + }, }); const { result } = renderHook(() => useSetupTechnology({ @@ -225,12 +238,19 @@ describe('useSetupTechnology', () => { }); it('should create agentless policy if agentless feature is enabled and isCloud is true and agentless.api.url', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: true, - isDefaultAgentlessPolicyEnabled: false, + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + api: { + url: 'https://agentless.api.url', + }, + }, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isCloudEnabled: true, + }, }); - const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -256,12 +276,19 @@ describe('useSetupTechnology', () => { }); it('should update agentless policy name to match integration name if agentless is enabled', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: true, - isDefaultAgentlessPolicyEnabled: false, + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + api: { + url: 'https://agentless.api.url', + }, + }, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isCloudEnabled: true, + }, }); - const { result, rerender } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -304,10 +331,11 @@ describe('useSetupTechnology', () => { }); it('should not create agentless policy if agentless feature is enabled and isCloud is true and agentless.api.url is not defined', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: false, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: false, + (useConfig as MockFn).mockReturnValue({} as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isCloudEnabled: true, + }, }); const { result, waitForNextUpdate } = renderHook(() => @@ -331,10 +359,10 @@ describe('useSetupTechnology', () => { }); it('should not fetch agentless policy if agentless is enabled but serverless is disabled', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: false, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: false, + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: false, + }, }); const { result } = renderHook(() => @@ -352,12 +380,6 @@ describe('useSetupTechnology', () => { }); it('should update agent policy and selected policy tab when setup technology is agentless', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: true, - }); - const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -379,12 +401,6 @@ describe('useSetupTechnology', () => { }); it('should update new agent policy and selected policy tab when setup technology is agent-based', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: true, - }); - const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -416,11 +432,9 @@ describe('useSetupTechnology', () => { }); it('should not update agent policy and selected policy tab when agentless is disabled', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: false, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: false, - }); + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); const { result } = renderHook(() => useSetupTechnology({ @@ -442,12 +456,6 @@ describe('useSetupTechnology', () => { }); it('should not update agent policy and selected policy tab when setup technology matches the current one ', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: true, - }); - const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, @@ -473,12 +481,6 @@ describe('useSetupTechnology', () => { }); it('should revert the agent policy name to the original value when switching from agentless back to agent-based', async () => { - (useAgentless as MockFn).mockReturnValue({ - isAgentlessEnabled: true, - isAgentlessApiEnabled: false, - isDefaultAgentlessPolicyEnabled: true, - }); - const { result, waitForNextUpdate } = renderHook(() => useSetupTechnology({ setNewAgentPolicy, From 0a89826f2c374430bb7361cdbc8b6d69cc32ba9a Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 11 Oct 2024 15:09:54 -0700 Subject: [PATCH 12/20] Try reverting file moves --- ...per.test.ts => agentless_policy_helper.test.ts} | 4 ++-- .../components/package_policy_input_panel.test.tsx | 6 +----- .../single_page_layout/hooks/setup_technology.ts | 14 +++++--------- .../screens/home/hooks/use_available_packages.tsx | 4 ++-- .../fleet/server/services/utils/agentless.ts | 2 +- 5 files changed, 11 insertions(+), 19 deletions(-) rename x-pack/plugins/fleet/common/services/{agentless_helper.test.ts => agentless_policy_helper.test.ts} (98%) diff --git a/x-pack/plugins/fleet/common/services/agentless_helper.test.ts b/x-pack/plugins/fleet/common/services/agentless_policy_helper.test.ts similarity index 98% rename from x-pack/plugins/fleet/common/services/agentless_helper.test.ts rename to x-pack/plugins/fleet/common/services/agentless_policy_helper.test.ts index 0ed5926e75506..aed3020c9dcf1 100644 --- a/x-pack/plugins/fleet/common/services/agentless_helper.test.ts +++ b/x-pack/plugins/fleet/common/services/agentless_policy_helper.test.ts @@ -12,9 +12,9 @@ import { getAgentlessAgentPolicyNameFromPackagePolicyName, isOnlyAgentlessIntegration, isOnlyAgentlessPolicyTemplate, -} from './agentless_helper'; +} from './agentless_policy_helper'; -describe('agentless_helper', () => { +describe('agentless_policy_helper', () => { describe('isAgentlessIntegration', () => { it('should return true if packageInfo is defined and has at least one agentless integration', () => { const packageInfo = { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx index c4cfa4f62e922..e09467d9a5c50 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx @@ -10,7 +10,7 @@ import { waitFor } from '@testing-library/react'; import { createFleetTestRendererMock } from '../../../../../../../../mock'; import type { TestRenderer } from '../../../../../../../../mock'; -import { useAgentless } from '../../../../../../../../hooks'; +import { useAgentless } from '../../../single_page_layout/hooks/setup_technology'; import type { PackageInfo, @@ -365,8 +365,6 @@ describe('PackagePolicyInputPanel', () => { isAgentlessIntegration: jest.fn(), isAgentlessApiEnabled: true, isDefaultAgentlessPolicyEnabled: false, - isOnlyAgentlessIntegration: jest.fn(), - isOnlyAgentlessPolicyTemplate: jest.fn(), }); }); @@ -402,8 +400,6 @@ describe('PackagePolicyInputPanel', () => { isAgentlessIntegration: jest.fn(), isAgentlessApiEnabled: true, isDefaultAgentlessPolicyEnabled: false, - isOnlyAgentlessIntegration: jest.fn(), - isOnlyAgentlessPolicyTemplate: jest.fn(), }); }); 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 fb6aefcf7583e..07dc4817601d7 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 @@ -20,7 +20,10 @@ import { SetupTechnology } from '../../../../../types'; import { sendGetOneAgentPolicy, useStartServices } from '../../../../../hooks'; import { SelectedPolicyTab } from '../../components'; import { AGENTLESS_POLICY_ID } from '../../../../../../../../common/constants'; -import { getAgentlessAgentPolicyNameFromPackagePolicyName } from '../../../../../../../../common/services/agentless_policy_helper'; +import { + isAgentlessIntegration as isAgentlessIntegrationFn, + getAgentlessAgentPolicyNameFromPackagePolicyName, +} from '../../../../../../../../common/services/agentless_policy_helper'; export const useAgentless = () => { const config = useConfig(); @@ -45,14 +48,7 @@ export const useAgentless = () => { // When an integration has at least a policy template enabled for agentless const isAgentlessIntegration = (packageInfo: PackageInfo | undefined) => { - if ( - isAgentlessEnabled && - packageInfo?.policy_templates && - packageInfo?.policy_templates.length > 0 && - !!packageInfo?.policy_templates.find( - (policyTemplate) => policyTemplate?.deployment_modes?.agentless.enabled === true - ) - ) { + if (isAgentlessEnabled && isAgentlessIntegrationFn(packageInfo)) { return true; } return false; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx index f5e4475f58b3d..2f506b30b2626 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx @@ -11,7 +11,7 @@ import { uniq } from 'lodash'; import type { CustomIntegration } from '@kbn/custom-integrations-plugin/common'; import type { IntegrationPreferenceType } from '../../../components/integration_preference'; -import { useAgentless } from '../../../../../../../hooks'; +import { useAgentless } from '../../../../../../fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology'; import { useGetPackagesQuery, useGetCategoriesQuery, @@ -33,7 +33,7 @@ import { import { isOnlyAgentlessPolicyTemplate, isOnlyAgentlessIntegration, -} from '../../../../../../../../common/services/agentless_helper'; +} from '../../../../../../../../common/services/agentless_policy_helper'; import type { IntegrationCardItem } from '..'; diff --git a/x-pack/plugins/fleet/server/services/utils/agentless.ts b/x-pack/plugins/fleet/server/services/utils/agentless.ts index 018ce48aa1793..d8c1c3bf08001 100644 --- a/x-pack/plugins/fleet/server/services/utils/agentless.ts +++ b/x-pack/plugins/fleet/server/services/utils/agentless.ts @@ -7,7 +7,7 @@ import { appContextService } from '..'; import type { FleetConfigType } from '../../config'; -export { isAgentlessIntegration } from '../../../common/services/agentless_helper'; +export { isAgentlessIntegration } from '../../../common/services/agentless_policy_helper'; export const isAgentlessApiEnabled = () => { const cloudSetup = appContextService.getCloud && appContextService.getCloud(); From 32691a6c7374f7e7b787d3047bc2dfaf01c437eb Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 11 Oct 2024 15:14:52 -0700 Subject: [PATCH 13/20] Fix test and imports --- .../steps/components/package_policy_input_panel.test.tsx | 2 +- x-pack/plugins/fleet/server/services/epm/packages/install.ts | 4 ++-- x-pack/plugins/fleet/server/services/utils/agentless.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx index e09467d9a5c50..5accdf37e95e7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx @@ -21,7 +21,7 @@ import type { import { shouldShowStreamsByDefault, PackagePolicyInputPanel } from './package_policy_input_panel'; -jest.mock('../../../../../../../../hooks', () => { +jest.mock('../../../single_page_layout/hooks/setup_technology', () => { return { useAgentless: jest.fn(), }; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index ce472eb94e528..9fd9c40780a12 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -83,7 +83,7 @@ import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; import { auditLoggingService } from '../../audit_logging'; import { getFilteredInstallPackages } from '../filtered_packages'; -import { isAgentlessEnabled, isAgentlessIntegration } from '../../utils/agentless'; +import { isAgentlessEnabled, isOnlyAgentlessIntegration } from '../../utils/agentless'; import { _stateMachineInstallPackage } from './install_state_machine/_state_machine_package_install'; @@ -512,7 +512,7 @@ async function installPackageFromRegistry({ } // only allow install of agentless packages if agentless is enabled, or if using force flag - if (!isAgentlessEnabled() && isAgentlessIntegration(packageInfo)) { + if (!isAgentlessEnabled() && isOnlyAgentlessIntegration(packageInfo)) { if (!force) { throw new PackageInvalidDeploymentMode( `${pkgkey} contains agentless policy templates, agentless is not available on this deployment` diff --git a/x-pack/plugins/fleet/server/services/utils/agentless.ts b/x-pack/plugins/fleet/server/services/utils/agentless.ts index d8c1c3bf08001..c43f10db16b46 100644 --- a/x-pack/plugins/fleet/server/services/utils/agentless.ts +++ b/x-pack/plugins/fleet/server/services/utils/agentless.ts @@ -7,7 +7,7 @@ import { appContextService } from '..'; import type { FleetConfigType } from '../../config'; -export { isAgentlessIntegration } from '../../../common/services/agentless_policy_helper'; +export { isOnlyAgentlessIntegration } from '../../../common/services/agentless_policy_helper'; export const isAgentlessApiEnabled = () => { const cloudSetup = appContextService.getCloud && appContextService.getCloud(); From 822a53ede4b303610828c4c3270d07c640aceb8f Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 11 Oct 2024 15:35:41 -0700 Subject: [PATCH 14/20] Add unit tests for install path --- .../services/epm/packages/install.test.ts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index a0bd8c8d77fe6..18acec2c78c02 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -17,6 +17,7 @@ import { licenseService } from '../../license'; import { auditLoggingService } from '../../audit_logging'; import { appContextService } from '../../app_context'; import { ConcurrentInstallOperationError, FleetError, PackageNotFoundError } from '../../../errors'; +import { isAgentlessEnabled, isOnlyAgentlessIntegration } from '../../utils/agentless'; import * as Registry from '../registry'; import { dataStreamService } from '../../data_streams'; @@ -102,6 +103,13 @@ jest.mock('../archive', () => { }); jest.mock('../../audit_logging'); +jest.mock('../../utils/agentless', () => { + return { + isAgentlessEnabled: jest.fn(), + isOnlyAgentlessIntegration: jest.fn(), + }; +}); + const mockGetBundledPackageByPkgKey = jest.mocked(getBundledPackageByPkgKey); const mockedAuditLoggingService = jest.mocked(auditLoggingService); @@ -357,6 +365,57 @@ describe('install', () => { expect(response.status).toEqual('already_installed'); }); + describe('agentless', () => { + it('should not allow to install agentless only integration if agentless is not enabled', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + jest.mocked(isAgentlessEnabled).mockReturnValueOnce(false); + jest.mocked(isOnlyAgentlessIntegration).mockReturnValueOnce(true); + + const response = await installPackage({ + spaceId: DEFAULT_SPACE_ID, + installSource: 'registry', + pkgkey: 'test_package', + savedObjectsClient: savedObjectsClientMock.create(), + esClient: {} as ElasticsearchClient, + }); + expect(response.error).toBeDefined(); + expect(response.error!.message).toEqual( + 'test_package contains agentless policy templates, agentless is not available on this deployment' + ); + }); + + it('should allow to install agentless only integration if agentless is not enabled but using force flag', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + jest.mocked(isAgentlessEnabled).mockReturnValueOnce(false); + jest.mocked(isOnlyAgentlessIntegration).mockReturnValueOnce(true); + + const response = await installPackage({ + spaceId: DEFAULT_SPACE_ID, + installSource: 'registry', + pkgkey: 'test_package', + savedObjectsClient: savedObjectsClientMock.create(), + esClient: {} as ElasticsearchClient, + force: true, + }); + expect(response.error).toBeUndefined(); + }); + + it('should allow to install agentless only integration if agentless is enabled', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + jest.mocked(isAgentlessEnabled).mockReturnValueOnce(true); + jest.mocked(isOnlyAgentlessIntegration).mockReturnValueOnce(true); + + const response = await installPackage({ + spaceId: DEFAULT_SPACE_ID, + installSource: 'registry', + pkgkey: 'test_package', + savedObjectsClient: savedObjectsClientMock.create(), + esClient: {} as ElasticsearchClient, + }); + expect(response.error).toBeUndefined(); + }); + }); + // failing it('should allow to install fleet_server if internal.fleetServerStandalone is configured', async () => { jest.mocked(appContextService.getConfig).mockReturnValueOnce({ From ee69611116d08b3e3bb7bd1b3f1fa970f404419f Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 14 Oct 2024 11:02:38 -0700 Subject: [PATCH 15/20] Fix test --- .../plugins/fleet/server/services/epm/packages/install.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index 18acec2c78c02..f2c5c70d046ee 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -416,8 +416,9 @@ describe('install', () => { }); }); - // failing it('should allow to install fleet_server if internal.fleetServerStandalone is configured', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + jest.mocked(isOnlyAgentlessIntegration).mockReturnValueOnce(false); jest.mocked(appContextService.getConfig).mockReturnValueOnce({ internal: { fleetServerStandalone: true, From c849bfcafa1952e1f6e647c8614d0f1f834cdec0 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 14 Oct 2024 13:38:06 -0700 Subject: [PATCH 16/20] Fix test attempt 2 --- .../server/services/epm/packages/install.test.ts | 11 +++++++++-- .../fleet/server/services/epm/packages/install.ts | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index f2c5c70d046ee..709e0d84d70fc 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -366,6 +366,13 @@ describe('install', () => { }); describe('agentless', () => { + beforeEach(() => { + jest.mocked(appContextService.getConfig).mockClear(); + jest.spyOn(licenseService, 'hasAtLeast').mockClear(); + jest.mocked(isAgentlessEnabled).mockClear(); + jest.mocked(isOnlyAgentlessIntegration).mockClear(); + }); + it('should not allow to install agentless only integration if agentless is not enabled', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); jest.mocked(isAgentlessEnabled).mockReturnValueOnce(false); @@ -417,13 +424,13 @@ describe('install', () => { }); it('should allow to install fleet_server if internal.fleetServerStandalone is configured', async () => { - jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); - jest.mocked(isOnlyAgentlessIntegration).mockReturnValueOnce(false); jest.mocked(appContextService.getConfig).mockReturnValueOnce({ internal: { fleetServerStandalone: true, }, } as any); + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValueOnce(true); + jest.mocked(isOnlyAgentlessIntegration).mockReturnValueOnce(false); const response = await installPackage({ spaceId: DEFAULT_SPACE_ID, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 9fd9c40780a12..1ea6f29cad839 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -512,7 +512,9 @@ async function installPackageFromRegistry({ } // only allow install of agentless packages if agentless is enabled, or if using force flag - if (!isAgentlessEnabled() && isOnlyAgentlessIntegration(packageInfo)) { + const agentlessEnabled = isAgentlessEnabled(); + const agentlessOnlyIntegration = isOnlyAgentlessIntegration(packageInfo); + if (!agentlessEnabled && agentlessOnlyIntegration) { if (!force) { throw new PackageInvalidDeploymentMode( `${pkgkey} contains agentless policy templates, agentless is not available on this deployment` From 3f1dc6695ca0baaa4543afbbd03bcf4ae583177f Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 14 Oct 2024 14:15:22 -0700 Subject: [PATCH 17/20] Allow fleet config to be null --- .../public/applications/fleet/layouts/default/default.tsx | 4 ++-- .../components/agent_policy_advanced_fields/index.tsx | 2 +- .../agent_policy/components/agent_policy_delete_provider.tsx | 5 ++--- .../steps/components/package_policy_input_stream.tsx | 2 +- .../single_page_layout/hooks/setup_technology.ts | 2 +- .../create_package_policy_page/single_page_layout/index.tsx | 5 ++--- .../agent_policy/details_page/components/settings/index.tsx | 5 ++--- .../sections/agent_policy/edit_package_policy_page/index.tsx | 5 ++--- .../fleet/sections/agent_policy/list_page/index.tsx | 5 ++--- .../agent_list_page/components/agent_soft_limit_callout.tsx | 2 +- .../agents/agent_list_page/hooks/use_agent_soft_limit.tsx | 2 +- .../public/applications/fleet/sections/agents/index.tsx | 4 ++-- .../public/components/package_policy_delete_provider.tsx | 5 ++--- x-pack/plugins/fleet/public/hooks/use_config.ts | 5 +++-- .../fleet/public/hooks/use_fleet_server_standalone.tsx | 2 +- x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx | 2 +- 16 files changed, 26 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx b/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx index 5c25b70f7b67c..59a8052695416 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx @@ -30,7 +30,7 @@ export const DefaultLayout: React.FunctionComponent = ({ rightColumn, }) => { const { getHref } = useLink(); - const { agents } = useConfig(); + const config = useConfig(); const authz = useAuthz(); const { agentTamperProtectionEnabled, subfeaturePrivileges } = ExperimentalFeaturesService.get(); @@ -44,7 +44,7 @@ export const DefaultLayout: React.FunctionComponent = ({ ), isSelected: section === 'agents', href: getHref('agent_list'), - disabled: !agents?.enabled, + disabled: !config?.agents?.enabled, 'data-test-subj': 'fleet-agents-tab', isHidden: !authz.fleet.readAgents, }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx index 1497b1bb0589e..83f72cfc34f07 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx @@ -91,7 +91,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = const config = useConfig(); const authz = useAuthz(); const maxAgentPoliciesWithInactivityTimeout = - config.developer?.maxAgentPoliciesWithInactivityTimeout ?? + config?.developer?.maxAgentPoliciesWithInactivityTimeout ?? DEFAULT_MAX_AGENT_POLICIES_WITH_INACTIVITY_TIMEOUT; const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx index 32c350108bccf..27cd76a5e7cf3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx @@ -43,9 +43,8 @@ export const AgentPolicyDeleteProvider: React.FunctionComponent = ({ agentPolicy, }) => { const { notifications } = useStartServices(); - const { - agents: { enabled: isFleetEnabled }, - } = useConfig(); + const config = useConfig(); + const isFleetEnabled = config?.agents?.enabled ?? false; const [agentPolicyId, setAgentPolicyId] = useState(); const [isModalOpen, setIsModalOpen] = useState(false); const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState(false); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx index 8a10726b08df8..c712a7f4899ed 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx @@ -78,7 +78,7 @@ export const PackagePolicyInputStreamConfig = memo( }) => { const config = useConfig(); const isExperimentalDataStreamSettingsEnabled = - config.enableExperimental?.includes('experimentalDataStreamSettings') ?? false; + config?.enableExperimental?.includes('experimentalDataStreamSettings') ?? false; const { params: { packagePolicyId }, 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 241dcfbb93f4e..1b5c1d8a9e6f9 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 @@ -32,7 +32,7 @@ export const useAgentless = () => { const isServerless = !!cloud?.isServerlessEnabled; const isCloud = !!cloud?.isCloudEnabled; - const isAgentlessApiEnabled = (isCloud || isServerless) && config.agentless?.enabled; + const isAgentlessApiEnabled = (isCloud || isServerless) && config?.agentless?.enabled; const isDefaultAgentlessPolicyEnabled = !isAgentlessApiEnabled && isServerless && agentlessExperimentalFeatureEnabled; 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 24f8fa8a04fe5..713061d0d4d25 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 @@ -106,9 +106,8 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ queryParamsPolicyId, prerelease, }) => { - const { - agents: { enabled: isFleetEnabled }, - } = useConfig(); + const config = useConfig(); + const isFleetEnabled = config?.agents?.enabled ?? false; const hasFleetAddAgentsPrivileges = useAuthz().fleet.addAgents; const { params } = useRouteMatch(); const fleetStatus = useFleetStatus(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index 6e4f1e06b45a0..958f52a2030da 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -72,9 +72,8 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( ({ agentPolicy: originalAgentPolicy }) => { useBreadcrumbs('policy_details', { policyName: originalAgentPolicy.name }); const { notifications } = useStartServices(); - const { - agents: { enabled: isFleetEnabled }, - } = useConfig(); + const config = useConfig(); + const isFleetEnabled = config?.agents?.enabled ?? false; const hasAllAgentPoliciesPrivileges = useAuthz().fleet.allAgentPolicies; const refreshAgentPolicy = useAgentPolicyRefresh(); const [agentPolicy, setAgentPolicy] = useState({ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 6157f09968680..c34970e7ff055 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -98,9 +98,8 @@ export const EditPackagePolicyForm = memo<{ policyId?: string; }>(({ packagePolicyId, policyId, forceUpgrade = false, from = 'edit' }) => { const { application, notifications } = useStartServices(); - const { - agents: { enabled: isFleetEnabled }, - } = useConfig(); + const config = useConfig(); + const isFleetEnabled = config?.agents?.enabled ?? false; const { getHref } = useLink(); const { canUseMultipleAgentPolicies } = useMultipleAgentPolicies(); const { isAgentlessAgentPolicy, isAgentlessIntegration } = useAgentless(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx index 2682a5239071d..5eb0dd6fbd510 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx @@ -53,9 +53,8 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { const { getPath } = useLink(); const hasFleetAllAgentPoliciesPrivileges = useAuthz().fleet.allAgentPolicies; - const { - agents: { enabled: isFleetEnabled }, - } = useConfig(); + const config = useConfig(); + const isFleetEnabled = config?.agents?.enabled ?? false; // Table and search states const { urlParams, toUrlParams } = useUrlParams(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx index 68f986fc8c0f4..86bb3bda67851 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx @@ -29,7 +29,7 @@ export const AgentSoftLimitCallout = () => { id="xpack.fleet.agentSoftLimitCallout.calloutDescription" defaultMessage="Fleet supports a maximum of {nbAgents} active agents. You need to unenroll some agents to ensure that all active agents are able to connect and new agents can be enrolled." values={{ - nbAgents: , + nbAgents: , }} /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx index e693838ca47bf..42120ef5cde9b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx @@ -26,7 +26,7 @@ async function fetchTotalOnlineAgents() { export function useAgentSoftLimit() { const config = useConfig(); - const softLimit = config.internal?.activeAgentsSoftLimit; + const softLimit = config?.internal?.activeAgentsSoftLimit; const { data: totalAgents } = useQuery(['fetch-total-online-agents'], fetchTotalOnlineAgents, { enabled: softLimit !== undefined, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index 9adb388ada6d6..112835f174585 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -23,12 +23,12 @@ import { NoAccessPage } from './error_pages/no_access'; export const AgentsApp: React.FunctionComponent = () => { useBreadcrumbs('agent_list'); - const { agents } = useConfig(); + const config = useConfig(); const authz = useAuthz(); const fleetStatus = useFleetStatus(); const flyoutContext = useFlyoutContext(); - if (!agents.enabled) return null; + if (!config?.agents.enabled) return null; if (!fleetStatus.missingRequirements && fleetStatus.isLoading) { return ; } diff --git a/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx b/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx index f12c4cf371ed5..3741ab95393c8 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx @@ -38,9 +38,8 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent = ({ children, }) => { const { notifications } = useStartServices(); - const { - agents: { enabled: isFleetEnabled }, - } = useConfig(); + const config = useConfig(); + const isFleetEnabled = config?.agents?.enabled ?? false; const [packagePolicies, setPackagePolicies] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState(false); diff --git a/x-pack/plugins/fleet/public/hooks/use_config.ts b/x-pack/plugins/fleet/public/hooks/use_config.ts index db86ed66bba60..8567b828b1d34 100644 --- a/x-pack/plugins/fleet/public/hooks/use_config.ts +++ b/x-pack/plugins/fleet/public/hooks/use_config.ts @@ -11,10 +11,11 @@ import type { FleetConfigType } from '../plugin'; export const ConfigContext = React.createContext(null); -export function useConfig() { +export function useConfig(): FleetConfigType | null { const config = useContext(ConfigContext); if (config === null) { - throw new Error('ConfigContext not initialized'); + // eslint-disable-next-line no-console + console.error('Fleet ConfigContext not initialized'); } return config; } diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx index 2d4a53de12644..a9de8e9528ede 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx @@ -9,7 +9,7 @@ import { useConfig } from './use_config'; export function useFleetServerStandalone() { const config = useConfig(); - const isFleetServerStandalone = config.internal?.fleetServerStandalone ?? false; + const isFleetServerStandalone = config?.internal?.fleetServerStandalone ?? false; return { isFleetServerStandalone }; } diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx index 019940dc92238..7d5fd419abb41 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx @@ -59,7 +59,7 @@ export const FleetStatusProvider: React.FC<{ const state = { ...defaultFleetStatus, - enabled: config.agents.enabled, + enabled: Boolean(config?.agents.enabled), isLoading, isReady: (!isLoading && data?.isReady) ?? defaultFleetStatus?.isReady ?? false, missingRequirements: data?.missing_requirements, From 44e7db58d7b56ab1606620601fec3238cdedae03 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 14 Oct 2024 14:46:34 -0700 Subject: [PATCH 18/20] Allow fleet config to be read from start dependencies --- .../plugins/fleet/public/hooks/use_config.ts | 22 +++++++++++++++---- x-pack/plugins/fleet/public/hooks/use_core.ts | 7 +++--- x-pack/plugins/fleet/public/plugin.ts | 3 ++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/fleet/public/hooks/use_config.ts b/x-pack/plugins/fleet/public/hooks/use_config.ts index 8567b828b1d34..73f8aa34880e6 100644 --- a/x-pack/plugins/fleet/public/hooks/use_config.ts +++ b/x-pack/plugins/fleet/public/hooks/use_config.ts @@ -9,13 +9,27 @@ import React, { useContext } from 'react'; import type { FleetConfigType } from '../plugin'; +import { useStartServices } from '.'; + export const ConfigContext = React.createContext(null); -export function useConfig(): FleetConfigType | null { - const config = useContext(ConfigContext); - if (config === null) { +export function useConfig(): FleetConfigType { + const { fleet } = useStartServices(); + const baseConfig = useContext(ConfigContext); + + // Downstream plugins may set `fleet` as part of the Kibana context + // which means that the Fleet config is exposed in that way + const pluginConfig = fleet?.config; + const config = baseConfig || pluginConfig || null; + + if (baseConfig === null && pluginConfig) { // eslint-disable-next-line no-console - console.error('Fleet ConfigContext not initialized'); + console.error('Fleet ConfigContext not initialized, using from plugin context'); } + + if (!config) { + throw new Error('Fleet ConfigContext not initialized'); + } + return config; } diff --git a/x-pack/plugins/fleet/public/hooks/use_core.ts b/x-pack/plugins/fleet/public/hooks/use_core.ts index 0e65686ac38a7..314e7931eb363 100644 --- a/x-pack/plugins/fleet/public/hooks/use_core.ts +++ b/x-pack/plugins/fleet/public/hooks/use_core.ts @@ -7,10 +7,11 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; -import type { FleetStartServices } from '../plugin'; +import type { FleetStart, FleetStartServices } from '../plugin'; -export function useStartServices(): FleetStartServices { - const { services } = useKibana(); +// Downstream plugins may set `fleet` as part of the Kibana context +export function useStartServices(): FleetStartServices & { fleet?: FleetStart } { + const { services } = useKibana(); if (services === null) { throw new Error('KibanaContextProvider not initialized'); } diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index ce922f838ae4e..ced047f7cc0c4 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -102,6 +102,7 @@ export interface FleetSetup {} export interface FleetStart { /** Authorization for the current user */ authz: FleetAuthz; + config: FleetConfigType; registerExtension: UIExtensionRegistrationCallback; isInitialized: () => Promise; hooks: { @@ -356,7 +357,7 @@ export class FleetPlugin implements Plugin { const permissionsResponse = await getPermissions(); From 7a565ae9d6ab243f89d1992d5191b2b87bf8186e Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 14 Oct 2024 14:47:44 -0700 Subject: [PATCH 19/20] Revert "Allow fleet config to be null" This reverts commit 3f1dc6695ca0baaa4543afbbd03bcf4ae583177f. --- .../public/applications/fleet/layouts/default/default.tsx | 4 ++-- .../components/agent_policy_advanced_fields/index.tsx | 2 +- .../agent_policy/components/agent_policy_delete_provider.tsx | 5 +++-- .../steps/components/package_policy_input_stream.tsx | 2 +- .../single_page_layout/hooks/setup_technology.ts | 2 +- .../create_package_policy_page/single_page_layout/index.tsx | 5 +++-- .../agent_policy/details_page/components/settings/index.tsx | 5 +++-- .../sections/agent_policy/edit_package_policy_page/index.tsx | 5 +++-- .../fleet/sections/agent_policy/list_page/index.tsx | 5 +++-- .../agent_list_page/components/agent_soft_limit_callout.tsx | 2 +- .../agents/agent_list_page/hooks/use_agent_soft_limit.tsx | 2 +- .../public/applications/fleet/sections/agents/index.tsx | 4 ++-- .../public/components/package_policy_delete_provider.tsx | 5 +++-- x-pack/plugins/fleet/public/hooks/use_config.ts | 2 +- .../fleet/public/hooks/use_fleet_server_standalone.tsx | 2 +- x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx | 2 +- 16 files changed, 30 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx b/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx index 59a8052695416..5c25b70f7b67c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/layouts/default/default.tsx @@ -30,7 +30,7 @@ export const DefaultLayout: React.FunctionComponent = ({ rightColumn, }) => { const { getHref } = useLink(); - const config = useConfig(); + const { agents } = useConfig(); const authz = useAuthz(); const { agentTamperProtectionEnabled, subfeaturePrivileges } = ExperimentalFeaturesService.get(); @@ -44,7 +44,7 @@ export const DefaultLayout: React.FunctionComponent = ({ ), isSelected: section === 'agents', href: getHref('agent_list'), - disabled: !config?.agents?.enabled, + disabled: !agents?.enabled, 'data-test-subj': 'fleet-agents-tab', isHidden: !authz.fleet.readAgents, }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx index 83f72cfc34f07..1497b1bb0589e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx @@ -91,7 +91,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = const config = useConfig(); const authz = useAuthz(); const maxAgentPoliciesWithInactivityTimeout = - config?.developer?.maxAgentPoliciesWithInactivityTimeout ?? + config.developer?.maxAgentPoliciesWithInactivityTimeout ?? DEFAULT_MAX_AGENT_POLICIES_WITH_INACTIVITY_TIMEOUT; const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx index 27cd76a5e7cf3..32c350108bccf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_delete_provider.tsx @@ -43,8 +43,9 @@ export const AgentPolicyDeleteProvider: React.FunctionComponent = ({ agentPolicy, }) => { const { notifications } = useStartServices(); - const config = useConfig(); - const isFleetEnabled = config?.agents?.enabled ?? false; + const { + agents: { enabled: isFleetEnabled }, + } = useConfig(); const [agentPolicyId, setAgentPolicyId] = useState(); const [isModalOpen, setIsModalOpen] = useState(false); const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState(false); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx index c712a7f4899ed..8a10726b08df8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx @@ -78,7 +78,7 @@ export const PackagePolicyInputStreamConfig = memo( }) => { const config = useConfig(); const isExperimentalDataStreamSettingsEnabled = - config?.enableExperimental?.includes('experimentalDataStreamSettings') ?? false; + config.enableExperimental?.includes('experimentalDataStreamSettings') ?? false; const { params: { packagePolicyId }, 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 1b5c1d8a9e6f9..241dcfbb93f4e 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 @@ -32,7 +32,7 @@ export const useAgentless = () => { const isServerless = !!cloud?.isServerlessEnabled; const isCloud = !!cloud?.isCloudEnabled; - const isAgentlessApiEnabled = (isCloud || isServerless) && config?.agentless?.enabled; + const isAgentlessApiEnabled = (isCloud || isServerless) && config.agentless?.enabled; const isDefaultAgentlessPolicyEnabled = !isAgentlessApiEnabled && isServerless && agentlessExperimentalFeatureEnabled; 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 713061d0d4d25..24f8fa8a04fe5 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 @@ -106,8 +106,9 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ queryParamsPolicyId, prerelease, }) => { - const config = useConfig(); - const isFleetEnabled = config?.agents?.enabled ?? false; + const { + agents: { enabled: isFleetEnabled }, + } = useConfig(); const hasFleetAddAgentsPrivileges = useAuthz().fleet.addAgents; const { params } = useRouteMatch(); const fleetStatus = useFleetStatus(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index 958f52a2030da..6e4f1e06b45a0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -72,8 +72,9 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( ({ agentPolicy: originalAgentPolicy }) => { useBreadcrumbs('policy_details', { policyName: originalAgentPolicy.name }); const { notifications } = useStartServices(); - const config = useConfig(); - const isFleetEnabled = config?.agents?.enabled ?? false; + const { + agents: { enabled: isFleetEnabled }, + } = useConfig(); const hasAllAgentPoliciesPrivileges = useAuthz().fleet.allAgentPolicies; const refreshAgentPolicy = useAgentPolicyRefresh(); const [agentPolicy, setAgentPolicy] = useState({ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index c34970e7ff055..6157f09968680 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -98,8 +98,9 @@ export const EditPackagePolicyForm = memo<{ policyId?: string; }>(({ packagePolicyId, policyId, forceUpgrade = false, from = 'edit' }) => { const { application, notifications } = useStartServices(); - const config = useConfig(); - const isFleetEnabled = config?.agents?.enabled ?? false; + const { + agents: { enabled: isFleetEnabled }, + } = useConfig(); const { getHref } = useLink(); const { canUseMultipleAgentPolicies } = useMultipleAgentPolicies(); const { isAgentlessAgentPolicy, isAgentlessIntegration } = useAgentless(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx index 5eb0dd6fbd510..2682a5239071d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx @@ -53,8 +53,9 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { const { getPath } = useLink(); const hasFleetAllAgentPoliciesPrivileges = useAuthz().fleet.allAgentPolicies; - const config = useConfig(); - const isFleetEnabled = config?.agents?.enabled ?? false; + const { + agents: { enabled: isFleetEnabled }, + } = useConfig(); // Table and search states const { urlParams, toUrlParams } = useUrlParams(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx index 86bb3bda67851..68f986fc8c0f4 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_soft_limit_callout.tsx @@ -29,7 +29,7 @@ export const AgentSoftLimitCallout = () => { id="xpack.fleet.agentSoftLimitCallout.calloutDescription" defaultMessage="Fleet supports a maximum of {nbAgents} active agents. You need to unenroll some agents to ensure that all active agents are able to connect and new agents can be enrolled." values={{ - nbAgents: , + nbAgents: , }} /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx index 42120ef5cde9b..e693838ca47bf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_agent_soft_limit.tsx @@ -26,7 +26,7 @@ async function fetchTotalOnlineAgents() { export function useAgentSoftLimit() { const config = useConfig(); - const softLimit = config?.internal?.activeAgentsSoftLimit; + const softLimit = config.internal?.activeAgentsSoftLimit; const { data: totalAgents } = useQuery(['fetch-total-online-agents'], fetchTotalOnlineAgents, { enabled: softLimit !== undefined, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index 112835f174585..9adb388ada6d6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -23,12 +23,12 @@ import { NoAccessPage } from './error_pages/no_access'; export const AgentsApp: React.FunctionComponent = () => { useBreadcrumbs('agent_list'); - const config = useConfig(); + const { agents } = useConfig(); const authz = useAuthz(); const fleetStatus = useFleetStatus(); const flyoutContext = useFlyoutContext(); - if (!config?.agents.enabled) return null; + if (!agents.enabled) return null; if (!fleetStatus.missingRequirements && fleetStatus.isLoading) { return ; } diff --git a/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx b/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx index 3741ab95393c8..f12c4cf371ed5 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx @@ -38,8 +38,9 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent = ({ children, }) => { const { notifications } = useStartServices(); - const config = useConfig(); - const isFleetEnabled = config?.agents?.enabled ?? false; + const { + agents: { enabled: isFleetEnabled }, + } = useConfig(); const [packagePolicies, setPackagePolicies] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState(false); diff --git a/x-pack/plugins/fleet/public/hooks/use_config.ts b/x-pack/plugins/fleet/public/hooks/use_config.ts index 73f8aa34880e6..2df3ed5f38a54 100644 --- a/x-pack/plugins/fleet/public/hooks/use_config.ts +++ b/x-pack/plugins/fleet/public/hooks/use_config.ts @@ -24,7 +24,7 @@ export function useConfig(): FleetConfigType { if (baseConfig === null && pluginConfig) { // eslint-disable-next-line no-console - console.error('Fleet ConfigContext not initialized, using from plugin context'); + console.warn('Fleet ConfigContext not initialized, using from plugin context'); } if (!config) { diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx index a9de8e9528ede..2d4a53de12644 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_server_standalone.tsx @@ -9,7 +9,7 @@ import { useConfig } from './use_config'; export function useFleetServerStandalone() { const config = useConfig(); - const isFleetServerStandalone = config?.internal?.fleetServerStandalone ?? false; + const isFleetServerStandalone = config.internal?.fleetServerStandalone ?? false; return { isFleetServerStandalone }; } diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx index 7d5fd419abb41..019940dc92238 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx @@ -59,7 +59,7 @@ export const FleetStatusProvider: React.FC<{ const state = { ...defaultFleetStatus, - enabled: Boolean(config?.agents.enabled), + enabled: config.agents.enabled, isLoading, isReady: (!isLoading && data?.isReady) ?? defaultFleetStatus?.isReady ?? false, missingRequirements: data?.missing_requirements, From c0372a572f2ecb5f9ef22a139ec2fe1dc4146916 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 14 Oct 2024 14:50:19 -0700 Subject: [PATCH 20/20] Fix plugin start mock --- x-pack/plugins/fleet/public/mock/plugin_interfaces.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts b/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts index e2490eecfd766..5af34f2b0bc04 100644 --- a/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts +++ b/x-pack/plugins/fleet/public/mock/plugin_interfaces.ts @@ -9,6 +9,7 @@ import type { UIExtensionsStorage } from '../types'; import { createExtensionRegistrationCallback } from '../services/ui_extensions'; import type { MockedFleetStart } from './types'; +import { createConfigurationMock } from './plugin_configuration'; export const createStartMock = (extensionsStorage: UIExtensionsStorage = {}): MockedFleetStart => { return { @@ -41,6 +42,7 @@ export const createStartMock = (extensionsStorage: UIExtensionsStorage = {}): Mo writeIntegrationPolicies: true, }, }, + config: createConfigurationMock(), hooks: { epm: { getBulkAssets: jest.fn() } }, }; };