From 9b854f3e23b06c9198ea9fe4ad248356aa2b1948 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 21 Nov 2024 16:57:06 -0800 Subject: [PATCH 1/3] [UII] Allow to create integration policy with no agent policies (#201051) ## Summary Resolves #198165. This PR fixes an issue where `policy_ids` array was not able to be cleared behind the scenes to create an orphaned integration policy, even when cluster is able to use the multiple agent policies feature. (cherry picked from commit 3952dea3962c52dbd8d0cf01f7966670a64639d4) # Conflicts: # x-pack/plugins/fleet/cypress/screens/integrations.ts # x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.test.tsx # x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/components/step_edit_hosts.tsx --- .../fleet/cypress/e2e/package_policy.cy.ts | 87 +++++++++++++------ .../fleet/cypress/screens/integrations.ts | 3 +- .../steps/step_select_hosts.test.tsx | 3 +- .../components/steps/step_select_hosts.tsx | 10 +-- .../single_page_layout/hooks/form.tsx | 4 +- .../services/devtools_request.tsx | 7 +- .../hooks/use_multiple_agent_policies.ts | 11 +-- 7 files changed, 78 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts b/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts index 271d2ba7b871d..1c1191e32ded1 100644 --- a/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts @@ -4,36 +4,46 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { EXISTING_HOSTS_TAB } from '../screens/fleet'; +import { + ADD_INTEGRATION_POLICY_BTN, + CREATE_PACKAGE_POLICY_SAVE_BTN, + POLICY_EDITOR, +} from '../screens/integrations'; +import { CONFIRM_MODAL } from '../screens/navigation'; import { login } from '../tasks/login'; -describe('Edit package policy', () => { - const policyConfig = { - id: 'policy-1', - name: 'fleet_server-1', - namespace: 'default', - package: { name: 'fleet_server', title: 'Fleet Server', version: '1.1.0' }, - enabled: true, - policy_id: 'fleet-server-policy', - policy_ids: ['fleet-server-policy'], - output_id: 'fleet-default-output', - inputs: [ - { - type: 'fleet-server', - policy_template: 'fleet_server', - enabled: true, - streams: [], - vars: { - host: { value: ['0.0.0.0'], type: 'text' }, - port: { value: [8220], type: 'integer' }, - max_connections: { type: 'integer' }, - custom: { value: '', type: 'yaml' }, - }, - compiled_input: { server: { port: 8220, host: '0.0.0.0' } }, - }, - ], - }; +describe('Package policy', () => { beforeEach(() => { login(); + }); + + it('should edit package policy', () => { + const policyConfig = { + id: 'policy-1', + name: 'fleet_server-1', + namespace: 'default', + package: { name: 'fleet_server', title: 'Fleet Server', version: '1.1.0' }, + enabled: true, + policy_id: 'fleet-server-policy', + policy_ids: ['fleet-server-policy'], + output_id: 'fleet-default-output', + inputs: [ + { + type: 'fleet-server', + policy_template: 'fleet_server', + enabled: true, + streams: [], + vars: { + host: { value: ['0.0.0.0'], type: 'text' }, + port: { value: [8220], type: 'integer' }, + max_connections: { type: 'integer' }, + custom: { value: '', type: 'yaml' }, + }, + compiled_input: { server: { port: 8220, host: '0.0.0.0' } }, + }, + ], + }; cy.intercept('/api/fleet/package_policies/policy-1', { item: policyConfig, @@ -111,9 +121,7 @@ describe('Edit package policy', () => { status: 'not_installed', }, }); - }); - it('should edit package policy', () => { cy.visit('/app/fleet/policies/fleet-server-policy/edit-integration/policy-1'); cy.getBySel('packagePolicyDescriptionInput').clear().type('desc'); @@ -128,4 +136,27 @@ describe('Edit package policy', () => { expect(interception.request.body.description).to.equal('desc'); }); }); + + it('should create a new orphaned package policy', () => { + cy.visit('/app/integrations/detail/system'); + cy.getBySel(ADD_INTEGRATION_POLICY_BTN).click(); + cy.getBySel(EXISTING_HOSTS_TAB).click(); + cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).should('exist'); + cy.getBySel(POLICY_EDITOR.AGENT_POLICY_CLEAR).should('not.exist'); + cy.getBySel(POLICY_EDITOR.POLICY_NAME_INPUT).clear().type('system-orphaned-test'); + + cy.intercept({ + method: 'POST', + url: '/api/fleet/package_policies', + }).as('createPackagePolicy'); + + cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).should('be.enabled').click(); + cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); + + cy.wait('@createPackagePolicy').then((interception) => { + expect(interception.request.body.name).to.equal('system-orphaned-test'); + expect(interception.request.body.policy_id).to.equal(undefined); + expect(interception.request.body.policy_ids).to.deep.equal([]); + }); + }); }); diff --git a/x-pack/plugins/fleet/cypress/screens/integrations.ts b/x-pack/plugins/fleet/cypress/screens/integrations.ts index 7cc6aefdf89ec..8b29a95265ff2 100644 --- a/x-pack/plugins/fleet/cypress/screens/integrations.ts +++ b/x-pack/plugins/fleet/cypress/screens/integrations.ts @@ -38,7 +38,8 @@ export const SETTINGS = { export const POLICY_EDITOR = { POLICY_NAME_INPUT: 'packagePolicyNameInput', DATASET_SELECT: 'datasetComboBox', - AGENT_POLICY_SELECT: 'agentPolicySelect', + AGENT_POLICY_SELECT: 'agentPolicyMultiSelect', + AGENT_POLICY_CLEAR: 'comboBoxClearButton', INSPECT_PIPELINES_BTN: 'datastreamInspectPipelineBtn', EDIT_MAPPINGS_BTN: 'datastreamEditMappingsBtn', CREATE_MAPPINGS_BTN: 'datastreamAddCustomComponentTemplateBtn', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx index 1ba7f56273dc6..90da8ca8b67b9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.test.tsx @@ -112,7 +112,8 @@ describe('StepSelectHosts', () => { waitFor(() => { expect(renderResult.getByText('Agent policy 1')).toBeInTheDocument(); }); - expect(renderResult.queryByRole('tablist')).not.toBeInTheDocument(); + expect(renderResult.queryByRole('tablist')).toBeInTheDocument(); + expect(renderResult.getByText('Create agent policy')).toBeInTheDocument(); }); it('should display tabs with New hosts selected when agent policies exist', () => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.tsx index 4416b7340ef36..5e0d03fccc658 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_hosts.tsx @@ -121,7 +121,7 @@ export const StepSelectHosts: React.FunctionComponent = ({ const handleOnTabClick = (tab: EuiTabbedContentTab) => updateSelectedTab(tab.id as SelectedPolicyTab); - return existingAgentPolicies.length > 0 ? ( + return ( = ({ tabs={tabs} onTabClick={handleOnTabClick} /> - ) : ( - ); }; 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 02e364f3e3d30..09485c0658f15 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 @@ -279,7 +279,7 @@ export function useOnSubmit({ useEffect(() => { if ( - agentPolicies.length > 0 && + (canUseMultipleAgentPolicies || agentPolicies.length > 0) && !isEqual( agentPolicies.map((policy) => policy.id), packagePolicy.policy_ids @@ -289,7 +289,7 @@ export function useOnSubmit({ policy_ids: agentPolicies.map((policy) => policy.id), }); } - }, [packagePolicy, agentPolicies, updatePackagePolicy]); + }, [packagePolicy, agentPolicies, updatePackagePolicy, canUseMultipleAgentPolicies]); const onSaveNavigate = useOnSaveNavigate({ packagePolicy, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx index 8badac52213e4..aa866106e8421 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/services/devtools_request.tsx @@ -18,6 +18,7 @@ import type { UpdatePackagePolicy, UpdateAgentPolicyRequest, } from '../../../types'; +import { canUseMultipleAgentPolicies } from '../../../hooks'; function generateKibanaDevToolsRequest(method: string, path: string, body: any) { return `${method} kbn:${path}\n${JSON.stringify(body, null, 2)}\n`; @@ -49,9 +50,13 @@ export function generateCreateAgentPolicyDevToolsRequest( export function generateCreatePackagePolicyDevToolsRequest( packagePolicy: NewPackagePolicy & { force?: boolean } ) { + const canHaveNoAgentPolicies = canUseMultipleAgentPolicies(); + return generateKibanaDevToolsRequest('POST', packagePolicyRouteService.getCreatePath(), { policy_ids: - packagePolicy.policy_ids.length > 0 ? packagePolicy.policy_ids : [''], + packagePolicy.policy_ids.length > 0 || canHaveNoAgentPolicies + ? packagePolicy.policy_ids + : [''], package: formatPackage(packagePolicy.package), ...omit(packagePolicy, 'policy_ids', 'package', 'enabled'), inputs: formatInputs(packagePolicy.inputs), diff --git a/x-pack/plugins/fleet/public/hooks/use_multiple_agent_policies.ts b/x-pack/plugins/fleet/public/hooks/use_multiple_agent_policies.ts index 2adf814e8ffba..4a123d511e29a 100644 --- a/x-pack/plugins/fleet/public/hooks/use_multiple_agent_policies.ts +++ b/x-pack/plugins/fleet/public/hooks/use_multiple_agent_policies.ts @@ -7,17 +7,18 @@ import { ExperimentalFeaturesService } from '../services'; -import { useLicense } from './use_license'; +import { licenseService } from './use_license'; export const LICENCE_FOR_MULTIPLE_AGENT_POLICIES = 'enterprise'; export function useMultipleAgentPolicies() { - const licenseService = useLicense(); + return { canUseMultipleAgentPolicies: canUseMultipleAgentPolicies() }; +} + +export function canUseMultipleAgentPolicies() { const { enableReusableIntegrationPolicies } = ExperimentalFeaturesService.get(); const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES); - const canUseMultipleAgentPolicies = enableReusableIntegrationPolicies && hasEnterpriseLicence; - - return { canUseMultipleAgentPolicies }; + return Boolean(enableReusableIntegrationPolicies && hasEnterpriseLicence); } From 5968eba515e38144057bba43c197825985d8249f Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 25 Nov 2024 12:35:23 -0800 Subject: [PATCH 2/3] Fix missing import --- .../single_page_layout/hooks/form.tsx | 3 +++ 1 file changed, 3 insertions(+) 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 09485c0658f15..f0a30df63b5d1 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 @@ -25,6 +25,7 @@ import { sendCreatePackagePolicy, sendBulkInstallPackages, sendGetPackagePolicies, + useMultipleAgentPolicies, } from '../../../../../hooks'; import { isVerificationError, packageToPackagePolicy } from '../../../../../services'; import { @@ -152,6 +153,8 @@ export function useOnSubmit({ }) { const { notifications } = useStartServices(); const confirmForceInstall = useConfirmForceInstall(); + const { canUseMultipleAgentPolicies } = useMultipleAgentPolicies(); + // only used to store the resulting package policy once saved const [savedPackagePolicy, setSavedPackagePolicy] = useState(); // Form state From 40c1f224e68f474d08739be4b7a53875ba5de349 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 25 Nov 2024 13:47:13 -0800 Subject: [PATCH 3/3] Remove cypress tests (feature flag for multiple policies is not enabled) --- .../fleet/cypress/e2e/package_policy.cy.ts | 87 ++++++------------- .../fleet/cypress/screens/integrations.ts | 3 +- 2 files changed, 29 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts b/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts index 1c1191e32ded1..271d2ba7b871d 100644 --- a/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/package_policy.cy.ts @@ -4,46 +4,36 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EXISTING_HOSTS_TAB } from '../screens/fleet'; -import { - ADD_INTEGRATION_POLICY_BTN, - CREATE_PACKAGE_POLICY_SAVE_BTN, - POLICY_EDITOR, -} from '../screens/integrations'; -import { CONFIRM_MODAL } from '../screens/navigation'; import { login } from '../tasks/login'; -describe('Package policy', () => { +describe('Edit package policy', () => { + const policyConfig = { + id: 'policy-1', + name: 'fleet_server-1', + namespace: 'default', + package: { name: 'fleet_server', title: 'Fleet Server', version: '1.1.0' }, + enabled: true, + policy_id: 'fleet-server-policy', + policy_ids: ['fleet-server-policy'], + output_id: 'fleet-default-output', + inputs: [ + { + type: 'fleet-server', + policy_template: 'fleet_server', + enabled: true, + streams: [], + vars: { + host: { value: ['0.0.0.0'], type: 'text' }, + port: { value: [8220], type: 'integer' }, + max_connections: { type: 'integer' }, + custom: { value: '', type: 'yaml' }, + }, + compiled_input: { server: { port: 8220, host: '0.0.0.0' } }, + }, + ], + }; beforeEach(() => { login(); - }); - - it('should edit package policy', () => { - const policyConfig = { - id: 'policy-1', - name: 'fleet_server-1', - namespace: 'default', - package: { name: 'fleet_server', title: 'Fleet Server', version: '1.1.0' }, - enabled: true, - policy_id: 'fleet-server-policy', - policy_ids: ['fleet-server-policy'], - output_id: 'fleet-default-output', - inputs: [ - { - type: 'fleet-server', - policy_template: 'fleet_server', - enabled: true, - streams: [], - vars: { - host: { value: ['0.0.0.0'], type: 'text' }, - port: { value: [8220], type: 'integer' }, - max_connections: { type: 'integer' }, - custom: { value: '', type: 'yaml' }, - }, - compiled_input: { server: { port: 8220, host: '0.0.0.0' } }, - }, - ], - }; cy.intercept('/api/fleet/package_policies/policy-1', { item: policyConfig, @@ -121,7 +111,9 @@ describe('Package policy', () => { status: 'not_installed', }, }); + }); + it('should edit package policy', () => { cy.visit('/app/fleet/policies/fleet-server-policy/edit-integration/policy-1'); cy.getBySel('packagePolicyDescriptionInput').clear().type('desc'); @@ -136,27 +128,4 @@ describe('Package policy', () => { expect(interception.request.body.description).to.equal('desc'); }); }); - - it('should create a new orphaned package policy', () => { - cy.visit('/app/integrations/detail/system'); - cy.getBySel(ADD_INTEGRATION_POLICY_BTN).click(); - cy.getBySel(EXISTING_HOSTS_TAB).click(); - cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).should('exist'); - cy.getBySel(POLICY_EDITOR.AGENT_POLICY_CLEAR).should('not.exist'); - cy.getBySel(POLICY_EDITOR.POLICY_NAME_INPUT).clear().type('system-orphaned-test'); - - cy.intercept({ - method: 'POST', - url: '/api/fleet/package_policies', - }).as('createPackagePolicy'); - - cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).should('be.enabled').click(); - cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); - - cy.wait('@createPackagePolicy').then((interception) => { - expect(interception.request.body.name).to.equal('system-orphaned-test'); - expect(interception.request.body.policy_id).to.equal(undefined); - expect(interception.request.body.policy_ids).to.deep.equal([]); - }); - }); }); diff --git a/x-pack/plugins/fleet/cypress/screens/integrations.ts b/x-pack/plugins/fleet/cypress/screens/integrations.ts index 8b29a95265ff2..7cc6aefdf89ec 100644 --- a/x-pack/plugins/fleet/cypress/screens/integrations.ts +++ b/x-pack/plugins/fleet/cypress/screens/integrations.ts @@ -38,8 +38,7 @@ export const SETTINGS = { export const POLICY_EDITOR = { POLICY_NAME_INPUT: 'packagePolicyNameInput', DATASET_SELECT: 'datasetComboBox', - AGENT_POLICY_SELECT: 'agentPolicyMultiSelect', - AGENT_POLICY_CLEAR: 'comboBoxClearButton', + AGENT_POLICY_SELECT: 'agentPolicySelect', INSPECT_PIPELINES_BTN: 'datastreamInspectPipelineBtn', EDIT_MAPPINGS_BTN: 'datastreamEditMappingsBtn', CREATE_MAPPINGS_BTN: 'datastreamAddCustomComponentTemplateBtn',