diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts index bf1ee2d020f691..e9ce21173e94e0 100644 --- a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts @@ -29,7 +29,7 @@ import { import { ExternalCallback, FleetStartContract, - PostPackagePolicyDeleteCallback, + PostPackagePolicyPostDeleteCallback, PostPackagePolicyPostCreateCallback, } from '@kbn/fleet-plugin/server'; import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../common/constants'; @@ -298,9 +298,9 @@ describe('Cloud Security Posture Plugin', () => { const deletedPackagePolicyMock = deletePackagePolicyMock(); deletedPackagePolicyMock[0].package!.name = CLOUD_SECURITY_POSTURE_PACKAGE_NAME; - const packagePolicyPostDeleteCallbacks: PostPackagePolicyDeleteCallback[] = []; + const packagePolicyPostDeleteCallbacks: PostPackagePolicyPostDeleteCallback[] = []; fleetMock.registerExternalCallback.mockImplementation((...args) => { - if (args[0] === 'postPackagePolicyDelete') { + if (args[0] === 'packagePolicyPostDelete') { packagePolicyPostDeleteCallbacks.push(args[1]); } }); diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.ts b/x-pack/plugins/cloud_security_posture/server/plugin.ts index 915d9b6138ad01..027f92b293d786 100755 --- a/x-pack/plugins/cloud_security_posture/server/plugin.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.ts @@ -16,7 +16,7 @@ import type { } from '@kbn/core/server'; import type { DeepReadonly } from 'utility-types'; import type { - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, PackagePolicy, NewPackagePolicy, } from '@kbn/fleet-plugin/common'; @@ -145,8 +145,8 @@ export class CspPlugin ); plugins.fleet.registerExternalCallback( - 'postPackagePolicyDelete', - async (deletedPackagePolicies: DeepReadonly) => { + 'packagePolicyPostDelete', + async (deletedPackagePolicies: DeepReadonly) => { for (const deletedPackagePolicy of deletedPackagePolicies) { if (isCspPackage(deletedPackagePolicy.package?.name)) { const soClient = core.savedObjects.createInternalRepository(); diff --git a/x-pack/plugins/fleet/common/index.ts b/x-pack/plugins/fleet/common/index.ts index b347b5b29dd817..25b108e9ad0d59 100644 --- a/x-pack/plugins/fleet/common/index.ts +++ b/x-pack/plugins/fleet/common/index.ts @@ -95,7 +95,7 @@ export type { GetAgentPoliciesRequest, GetAgentPoliciesResponse, GetAgentPoliciesResponseItem, - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, GetPackagesResponse, BulkInstallPackagesResponse, FleetErrorResponse, diff --git a/x-pack/plugins/fleet/common/mocks.ts b/x-pack/plugins/fleet/common/mocks.ts index 526110e1331374..eb45689a73bb67 100644 --- a/x-pack/plugins/fleet/common/mocks.ts +++ b/x-pack/plugins/fleet/common/mocks.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { DeletePackagePoliciesResponse, NewPackagePolicy, PackagePolicy } from './types'; +import type { PostDeletePackagePoliciesResponse, NewPackagePolicy, PackagePolicy } from './types'; import type { FleetAuthz } from './authz'; import { ENDPOINT_PRIVILEGES } from './constants'; @@ -47,7 +47,7 @@ export const createPackagePolicyMock = (): PackagePolicy => { }; }; -export const deletePackagePolicyMock = (): DeletePackagePoliciesResponse => { +export const deletePackagePolicyMock = (): PostDeletePackagePoliciesResponse => { const newPackagePolicy = createNewPackagePolicyMock(); return [ { diff --git a/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts b/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts index c7de1bd489b475..8fd1dc35f8c629 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/package_policy.ts @@ -54,7 +54,9 @@ export interface DeletePackagePoliciesRequest { }; } -export type DeletePackagePoliciesResponse = Array<{ +export type DeletePackagePoliciesResponse = PackagePolicy[]; + +export type PostDeletePackagePoliciesResponse = Array<{ id: string; name?: string; success: boolean; diff --git a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts index 3b2b14960379f0..3e029f6257d2e6 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts @@ -14,7 +14,7 @@ import type { } from '../../types'; import type { DeletePackagePoliciesRequest, - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, GetPackagePoliciesRequest, GetPackagePoliciesResponse, GetOnePackagePolicyResponse, @@ -44,7 +44,7 @@ export const sendUpdatePackagePolicy = ( }; export const sendDeletePackagePolicy = (body: DeletePackagePoliciesRequest['body']) => { - return sendRequest({ + return sendRequest({ path: packagePolicyRouteService.getDeletePath(), method: 'post', body: JSON.stringify(body), diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 1e780d3926465a..1344eb5102ce47 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -27,7 +27,7 @@ export type { FleetSetupContract, FleetSetupDeps, FleetStartContract } from './p export type { ExternalCallback, PutPackagePolicyUpdateCallback, - PostPackagePolicyDeleteCallback, + PostPackagePolicyPostDeleteCallback, PostPackagePolicyCreateCallback, FleetRequestHandlerContext, PostPackagePolicyPostCreateCallback, diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 01d92b8c8f28b5..31662538711233 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -122,6 +122,7 @@ export const createPackagePolicyServiceMock = (): jest.Mocked - callbackType === 'postPackagePolicyDelete' + callbackType === 'packagePolicyPostDelete' ? Promise.resolve(undefined) : Promise.resolve(packagePolicy) ), diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index ca29e7c0d079b0..8735cdf08cafd0 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -37,7 +37,7 @@ import type { import type { BulkGetPackagePoliciesResponse, CreatePackagePolicyResponse, - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, NewPackagePolicy, UpgradePackagePolicyDryRunResponse, UpgradePackagePolicyResponse, @@ -50,6 +50,8 @@ import { simplifiedPackagePolicytoNewPackagePolicy } from '../../../common/servi import type { SimplifiedPackagePolicy } from '../../../common/services/simplified_package_policy_helper'; +export const isNotNull = (value: T | null): value is T => value !== null; + export const getPackagePoliciesHandler: FleetRequestHandler< undefined, TypeOf @@ -398,8 +400,30 @@ export const deletePackagePolicyHandler: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; + const logger = appContextService.getLogger(); + try { - const body: DeletePackagePoliciesResponse = await packagePolicyService.delete( + try { + const packagePolicies = await packagePolicyService.getByIDs( + soClient, + request.body.packagePolicyIds, + { ignoreMissing: true } + ); + + if (packagePolicies) { + await packagePolicyService.runExternalCallbacks( + 'packagePolicyDelete', + packagePolicies, + context, + request + ); + } + } catch (error) { + logger.error(`An error occurred executing external callback: ${error}`); + logger.error(error); + } + + const body: PostDeletePackagePoliciesResponse = await packagePolicyService.delete( soClient, esClient, request.body.packagePolicyIds, @@ -407,13 +431,12 @@ export const deletePackagePolicyHandler: RequestHandler< ); try { await packagePolicyService.runExternalCallbacks( - 'postPackagePolicyDelete', + 'packagePolicyPostDelete', body, context, request ); } catch (error) { - const logger = appContextService.getLogger(); logger.error(`An error occurred executing external callback: ${error}`); logger.error(error); } @@ -434,7 +457,25 @@ export const deleteOnePackagePolicyHandler: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; + const logger = appContextService.getLogger(); + try { + try { + const packagePolicy = await packagePolicyService.get( + soClient, + request.params.packagePolicyId + ); + await packagePolicyService.runExternalCallbacks( + 'packagePolicyDelete', + packagePolicy ? [packagePolicy] : [], + context, + request + ); + } catch (error) { + logger.error(`An error occurred executing external callback: ${error}`); + logger.error(error); + } + const res = await packagePolicyService.delete( soClient, esClient, @@ -454,13 +495,12 @@ export const deleteOnePackagePolicyHandler: RequestHandler< } try { await packagePolicyService.runExternalCallbacks( - 'postPackagePolicyDelete', + 'packagePolicyPostDelete', res, context, request ); } catch (error) { - const logger = appContextService.getLogger(); logger.error(`An error occurred executing external callback: ${error}`); logger.error(error); } diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index a2a0b691a6a53a..014764743ee35e 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -171,7 +171,7 @@ describe('agent policy', () => { it('should run package policy delete external callbacks', async () => { await agentPolicyService.delete(soClient, esClient, 'mocked'); - expect(packagePolicyService.runDeleteExternalCallbacks).toHaveBeenCalledWith([ + expect(packagePolicyService.runPostDeleteExternalCallbacks).toHaveBeenCalledWith([ { id: 'package-1' }, ]); }); @@ -195,7 +195,7 @@ describe('agent policy', () => { await agentPolicyService.delete(soClient, esClient, 'mocked', { force: true }); - expect(packagePolicyService.runDeleteExternalCallbacks).toHaveBeenCalledWith([ + expect(packagePolicyService.runPostDeleteExternalCallbacks).toHaveBeenCalledWith([ { id: 'package-1' }, ]); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index c66f16f61b5c57..ea8b3ff572e326 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -51,7 +51,7 @@ import type { FleetServerPolicy, Installation, Output, - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, PackageInfo, } from '../../common/types'; import { @@ -680,7 +680,10 @@ class AgentPolicyService { `Cannot delete agent policy ${id} that contains managed package policies` ); } - const deletedPackagePolicies: DeletePackagePoliciesResponse = + + await packagePolicyService.runDeleteExternalCallbacks(packagePolicies); + + const deletedPackagePolicies: PostDeletePackagePoliciesResponse = await packagePolicyService.delete( soClient, esClient, @@ -691,7 +694,7 @@ class AgentPolicyService { } ); try { - await packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies); + await packagePolicyService.runPostDeleteExternalCallbacks(deletedPackagePolicies); } catch (error) { const logger = appContextService.getLogger(); logger.error(`An error occurred executing external callback: ${error}`); diff --git a/x-pack/plugins/fleet/server/services/app_context.ts b/x-pack/plugins/fleet/server/services/app_context.ts index caf3991431c21e..c8c6c8b4c0e595 100644 --- a/x-pack/plugins/fleet/server/services/app_context.ts +++ b/x-pack/plugins/fleet/server/services/app_context.ts @@ -35,8 +35,9 @@ import type { ExperimentalFeatures } from '../../common/experimental_features'; import type { ExternalCallback, ExternalCallbacksStorage, - PostPackagePolicyCreateCallback, PostPackagePolicyDeleteCallback, + PostPackagePolicyCreateCallback, + PostPackagePolicyPostDeleteCallback, PostPackagePolicyPostCreateCallback, PutPackagePolicyUpdateCallback, } from '../types'; @@ -211,8 +212,10 @@ class AppContextService { | Set< T extends 'packagePolicyCreate' ? PostPackagePolicyCreateCallback - : T extends 'postPackagePolicyDelete' + : T extends 'packagePolicyDelete' ? PostPackagePolicyDeleteCallback + : T extends 'packagePolicyPostDelete' + ? PostPackagePolicyPostDeleteCallback : T extends 'packagePolicyPostCreate' ? PostPackagePolicyPostCreateCallback : PutPackagePolicyUpdateCallback @@ -222,8 +225,10 @@ class AppContextService { return this.externalCallbacks.get(type) as Set< T extends 'packagePolicyCreate' ? PostPackagePolicyCreateCallback - : T extends 'postPackagePolicyDelete' + : T extends 'packagePolicyDelete' ? PostPackagePolicyDeleteCallback + : T extends 'packagePolicyPostDelete' + ? PostPackagePolicyPostDeleteCallback : T extends 'packagePolicyPostCreate' ? PostPackagePolicyPostCreateCallback : PutPackagePolicyUpdateCallback diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 0097961c71ede0..faf7e41f153786 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -24,11 +24,12 @@ import type { PackageInfo, PackagePolicySOAttributes, AgentPolicySOAttributes, - PostPackagePolicyDeleteCallback, + PostPackagePolicyPostDeleteCallback, RegistryDataStream, PackagePolicyInputStream, PackagePolicy, PostPackagePolicyPostCreateCallback, + PostPackagePolicyDeleteCallback, } from '../types'; import { createPackagePolicyMock } from '../../common/mocks'; @@ -37,11 +38,12 @@ import type { PutPackagePolicyUpdateCallback, PostPackagePolicyCreateCallback } import { createAppContextStartContractMock, xpackMocks } from '../mocks'; import type { - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, InputsOverride, NewPackagePolicy, NewPackagePolicyInput, PackagePolicyPackage, + DeletePackagePoliciesResponse, } from '../../common/types'; import { packageToPackagePolicy } from '../../common/services'; @@ -2039,11 +2041,11 @@ describe('Package policy service', () => { it('should allow to delete a package policy', async () => {}); }); - describe('runDeleteExternalCallbacks', () => { - let callbackOne: jest.MockedFunction; - let callbackTwo: jest.MockedFunction; + describe('runPostDeleteExternalCallbacks', () => { + let callbackOne: jest.MockedFunction; + let callbackTwo: jest.MockedFunction; let callingOrder: string[]; - let deletedPackagePolicies: DeletePackagePoliciesResponse; + let deletedPackagePolicies: PostDeletePackagePoliciesResponse; beforeEach(() => { appContextService.start(createAppContextStartContractMock()); @@ -2058,8 +2060,8 @@ describe('Package policy service', () => { callbackTwo = jest.fn(async (deletedPolicies) => { callingOrder.push('two'); }); - appContextService.addExternalCallback('postPackagePolicyDelete', callbackOne); - appContextService.addExternalCallback('postPackagePolicyDelete', callbackTwo); + appContextService.addExternalCallback('packagePolicyPostDelete', callbackOne); + appContextService.addExternalCallback('packagePolicyPostDelete', callbackTwo); }); afterEach(() => { @@ -2067,7 +2069,7 @@ describe('Package policy service', () => { }); it('should execute external callbacks', async () => { - await packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies); + await packagePolicyService.runPostDeleteExternalCallbacks(deletedPackagePolicies); expect(callbackOne).toHaveBeenCalledWith(deletedPackagePolicies); expect(callbackTwo).toHaveBeenCalledWith(deletedPackagePolicies); @@ -2080,7 +2082,78 @@ describe('Package policy service', () => { throw new Error('foo'); }); await expect( - packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies) + packagePolicyService.runPostDeleteExternalCallbacks(deletedPackagePolicies) + ).rejects.toThrow(FleetError); + expect(callingOrder).toEqual(['one', 'two']); + }); + + it('should provide an array of errors encountered by running external callbacks', async () => { + let error: FleetError; + const callbackOneError = new Error('foo 1'); + const callbackTwoError = new Error('foo 2'); + + callbackOne.mockImplementation(async (deletedPolicies) => { + callingOrder.push('one'); + throw callbackOneError; + }); + callbackTwo.mockImplementation(async (deletedPolicies) => { + callingOrder.push('two'); + throw callbackTwoError; + }); + + await packagePolicyService + .runPostDeleteExternalCallbacks(deletedPackagePolicies) + .catch((e) => { + error = e; + }); + + expect(error!.message).toEqual( + '2 encountered while executing package post delete external callbacks' + ); + expect(error!.meta).toEqual([callbackOneError, callbackTwoError]); + expect(callingOrder).toEqual(['one', 'two']); + }); + }); + + describe('runDeleteExternalCallbacks', () => { + let callbackOne: jest.MockedFunction; + let callbackTwo: jest.MockedFunction; + let callingOrder: string[]; + let packagePolicies: DeletePackagePoliciesResponse; + + beforeEach(() => { + appContextService.start(createAppContextStartContractMock()); + callingOrder = []; + packagePolicies = [{ id: 'a' }, { id: 'a' }] as DeletePackagePoliciesResponse; + callbackOne = jest.fn(async (deletedPolicies) => { + callingOrder.push('one'); + }); + callbackTwo = jest.fn(async (deletedPolicies) => { + callingOrder.push('two'); + }); + appContextService.addExternalCallback('packagePolicyDelete', callbackOne); + appContextService.addExternalCallback('packagePolicyDelete', callbackTwo); + }); + + afterEach(() => { + appContextService.stop(); + }); + + it('should execute external callbacks', async () => { + await packagePolicyService.runDeleteExternalCallbacks(packagePolicies); + + expect(callbackOne).toHaveBeenCalledWith(packagePolicies); + expect(callbackTwo).toHaveBeenCalledWith(packagePolicies); + expect(callingOrder).toEqual(['one', 'two']); + }); + + it("should execute all external callbacks even if one throw's", async () => { + callbackOne.mockImplementation(async (deletedPolicies) => { + callingOrder.push('one'); + throw new Error('foo'); + }); + await expect( + packagePolicyService.runDeleteExternalCallbacks(packagePolicies) ).rejects.toThrow(FleetError); expect(callingOrder).toEqual(['one', 'two']); }); @@ -2099,7 +2172,7 @@ describe('Package policy service', () => { throw callbackTwoError; }); - await packagePolicyService.runDeleteExternalCallbacks(deletedPackagePolicies).catch((e) => { + await packagePolicyService.runDeleteExternalCallbacks(packagePolicies).catch((e) => { error = e; }); @@ -2286,7 +2359,7 @@ describe('Package policy service', () => { }); }); - describe('runPostPackagePolicyPostCreateCallback', () => { + describe('runPackagePolicyPostCreateCallback', () => { let context: ReturnType; let request: KibanaRequest; const packagePolicy = { diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 80d6341f5c68b6..0c824211ab2d55 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -44,7 +44,7 @@ import { PACKAGES_SAVED_OBJECT_TYPE, } from '../../common/constants'; import type { - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, UpgradePackagePolicyResponse, PackagePolicyInput, NewPackagePolicyInput, @@ -58,6 +58,7 @@ import type { PackagePolicyPackage, Installation, ExperimentalDataStreamFeature, + DeletePackagePoliciesResponse, } from '../../common/types'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../constants'; import { @@ -691,8 +692,8 @@ class PackagePolicyClientImpl implements PackagePolicyClient { esClient: ElasticsearchClient, ids: string[], options?: { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean; force?: boolean } - ): Promise { - const result: DeletePackagePoliciesResponse = []; + ): Promise { + const result: PostDeletePackagePoliciesResponse = []; const packagePolicies = await this.getByIDs(soClient, ids, { ignoreMissing: true }); if (!packagePolicies) { @@ -1205,15 +1206,19 @@ class PackagePolicyClientImpl implements PackagePolicyClient { public async runExternalCallbacks( externalCallbackType: A, - packagePolicy: A extends 'postPackagePolicyDelete' + packagePolicy: A extends 'packagePolicyDelete' ? DeletePackagePoliciesResponse + : A extends 'packagePolicyPostDelete' + ? PostDeletePackagePoliciesResponse : A extends 'packagePolicyPostCreate' ? PackagePolicy : NewPackagePolicy, context: RequestHandlerContext, request: KibanaRequest ): Promise< - A extends 'postPackagePolicyDelete' + A extends 'packagePolicyDelete' + ? void + : A extends 'packagePolicyPostDelete' ? void : A extends 'packagePolicyPostCreate' ? PackagePolicy @@ -1221,11 +1226,19 @@ class PackagePolicyClientImpl implements PackagePolicyClient { >; public async runExternalCallbacks( externalCallbackType: ExternalCallback[0], - packagePolicy: PackagePolicy | NewPackagePolicy | DeletePackagePoliciesResponse, + packagePolicy: + | PackagePolicy + | NewPackagePolicy + | PostDeletePackagePoliciesResponse + | DeletePackagePoliciesResponse, context: RequestHandlerContext, request: KibanaRequest ): Promise { - if (externalCallbackType === 'postPackagePolicyDelete') { + if (externalCallbackType === 'packagePolicyPostDelete') { + return await this.runPostDeleteExternalCallbacks( + packagePolicy as PostDeletePackagePoliciesResponse + ); + } else if (externalCallbackType === 'packagePolicyDelete') { return await this.runDeleteExternalCallbacks(packagePolicy as DeletePackagePoliciesResponse); } else { if (!Array.isArray(packagePolicy)) { @@ -1263,10 +1276,10 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } - public async runDeleteExternalCallbacks( - deletedPackagePolicies: DeletePackagePoliciesResponse + public async runPostDeleteExternalCallbacks( + deletedPackagePolicies: PostDeletePackagePoliciesResponse ): Promise { - const externalCallbacks = appContextService.getExternalCallbacks('postPackagePolicyDelete'); + const externalCallbacks = appContextService.getExternalCallbacks('packagePolicyPostDelete'); const errorsThrown: Error[] = []; if (externalCallbacks && externalCallbacks.size > 0) { @@ -1280,6 +1293,32 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } + if (errorsThrown.length > 0) { + throw new FleetError( + `${errorsThrown.length} encountered while executing package post delete external callbacks`, + errorsThrown + ); + } + } + } + + public async runDeleteExternalCallbacks( + deletedPackagePolices: DeletePackagePoliciesResponse + ): Promise { + const externalCallbacks = appContextService.getExternalCallbacks('packagePolicyDelete'); + const errorsThrown: Error[] = []; + + if (externalCallbacks && externalCallbacks.size > 0) { + for (const callback of externalCallbacks) { + // Failures from an external callback should not prevent other external callbacks from being + // executed. Errors (if any) will be collected and `throw`n after processing the entire set + try { + await callback(deletedPackagePolices); + } catch (error) { + errorsThrown.push(error); + } + } + if (errorsThrown.length > 0) { throw new FleetError( `${errorsThrown.length} encountered while executing package delete external callbacks`, diff --git a/x-pack/plugins/fleet/server/services/package_policy_service.ts b/x-pack/plugins/fleet/server/services/package_policy_service.ts index c1638e63b67c0d..790622a6ae6b4c 100644 --- a/x-pack/plugins/fleet/server/services/package_policy_service.ts +++ b/x-pack/plugins/fleet/server/services/package_policy_service.ts @@ -14,14 +14,17 @@ import type { import type { AuthenticatedUser } from '@kbn/security-plugin/server'; import type { - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, UpgradePackagePolicyResponse, PackageInfo, ListWithKuery, ListResult, UpgradePackagePolicyDryRunResponseItem, } from '../../common'; -import type { ExperimentalDataStreamFeature } from '../../common/types'; +import type { + DeletePackagePoliciesResponse, + ExperimentalDataStreamFeature, +} from '../../common/types'; import type { NewPackagePolicy, UpdatePackagePolicy, PackagePolicy } from '../types'; import type { ExternalCallback } from '..'; @@ -106,7 +109,7 @@ export interface PackagePolicyClient { esClient: ElasticsearchClient, ids: string[], options?: { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean; force?: boolean } - ): Promise; + ): Promise; upgrade( soClient: SavedObjectsClientContract, @@ -137,15 +140,19 @@ export interface PackagePolicyClient { runExternalCallbacks( externalCallbackType: A, - packagePolicy: A extends 'postPackagePolicyDelete' + packagePolicy: A extends 'packagePolicyDelete' ? DeletePackagePoliciesResponse + : A extends 'packagePolicyPostDelete' + ? PostDeletePackagePoliciesResponse : A extends 'packagePolicyPostCreate' ? PackagePolicy : NewPackagePolicy, context: RequestHandlerContext, request: KibanaRequest ): Promise< - A extends 'postPackagePolicyDelete' + A extends 'packagePolicyDelete' + ? void + : A extends 'packagePolicyPostDelete' ? void : A extends 'packagePolicyPostCreate' ? PackagePolicy @@ -154,6 +161,10 @@ export interface PackagePolicyClient { runDeleteExternalCallbacks(deletedPackagePolicies: DeletePackagePoliciesResponse): Promise; + runPostDeleteExternalCallbacks( + deletedPackagePolicies: PostDeletePackagePoliciesResponse + ): Promise; + getUpgradePackagePolicyInfo( soClient: SavedObjectsClientContract, id: string diff --git a/x-pack/plugins/fleet/server/types/extensions.ts b/x-pack/plugins/fleet/server/types/extensions.ts index 94682082822d5c..6d3ebc32b523f3 100644 --- a/x-pack/plugins/fleet/server/types/extensions.ts +++ b/x-pack/plugins/fleet/server/types/extensions.ts @@ -10,14 +10,19 @@ import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; import type { DeepReadonly } from 'utility-types'; import type { - DeletePackagePoliciesResponse, + PostDeletePackagePoliciesResponse, NewPackagePolicy, UpdatePackagePolicy, PackagePolicy, + DeletePackagePoliciesResponse, } from '../../common/types'; export type PostPackagePolicyDeleteCallback = ( - deletedPackagePolicies: DeepReadonly + packagePolicies: DeletePackagePoliciesResponse +) => Promise; + +export type PostPackagePolicyPostDeleteCallback = ( + deletedPackagePolicies: DeepReadonly ) => Promise; export type PostPackagePolicyCreateCallback = ( @@ -43,7 +48,12 @@ export type ExternalCallbackPostCreate = [ 'packagePolicyPostCreate', PostPackagePolicyPostCreateCallback ]; -export type ExternalCallbackDelete = ['postPackagePolicyDelete', PostPackagePolicyDeleteCallback]; + +export type ExternalCallbackDelete = ['packagePolicyDelete', PostPackagePolicyDeleteCallback]; +export type ExternalCallbackPostDelete = [ + 'packagePolicyPostDelete', + PostPackagePolicyPostDeleteCallback +]; export type ExternalCallbackUpdate = ['packagePolicyUpdate', PutPackagePolicyUpdateCallback]; /** @@ -53,6 +63,7 @@ export type ExternalCallback = | ExternalCallbackCreate | ExternalCallbackPostCreate | ExternalCallbackDelete + | ExternalCallbackPostDelete | ExternalCallbackUpdate; export type ExternalCallbacksStorage = Map>; diff --git a/x-pack/plugins/osquery/server/lib/fleet_integration.ts b/x-pack/plugins/osquery/server/lib/fleet_integration.ts index 6eac2b2efd9488..f03afedc8628ad 100644 --- a/x-pack/plugins/osquery/server/lib/fleet_integration.ts +++ b/x-pack/plugins/osquery/server/lib/fleet_integration.ts @@ -7,13 +7,13 @@ import type { SavedObjectReference, SavedObjectsClient } from '@kbn/core/server'; import { filter, map } from 'lodash'; -import type { PostPackagePolicyDeleteCallback } from '@kbn/fleet-plugin/server'; +import type { PostPackagePolicyPostDeleteCallback } from '@kbn/fleet-plugin/server'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import { packSavedObjectType } from '../../common/types'; import { OSQUERY_INTEGRATION_NAME } from '../../common'; export const getPackagePolicyDeleteCallback = - (packsClient: SavedObjectsClient): PostPackagePolicyDeleteCallback => + (packsClient: SavedObjectsClient): PostPackagePolicyPostDeleteCallback => async (deletedPackagePolicy) => { const deletedOsqueryManagerPolicies = filter(deletedPackagePolicy, [ 'package.name', diff --git a/x-pack/plugins/osquery/server/plugin.ts b/x-pack/plugins/osquery/server/plugin.ts index 365299750a9ce3..04417b15e22b83 100644 --- a/x-pack/plugins/osquery/server/plugin.ts +++ b/x-pack/plugins/osquery/server/plugin.ts @@ -160,7 +160,7 @@ export class OsqueryPlugin implements Plugin policy.id), }, }) - .catch(wrapErrorAndRejectPromise)) as AxiosResponse + .catch(wrapErrorAndRejectPromise)) as AxiosResponse ).data; } diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index bf87fbb22de0ae..8e7656150be23f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -140,7 +140,7 @@ export class EndpointAppContextService { ); registerIngestCallback( - 'postPackagePolicyDelete', + 'packagePolicyPostDelete', getPackagePolicyDeleteCallback(exceptionListsClient) ); } diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts index 44f9a8bafae3d1..e8bccd2aeacf72 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts @@ -43,7 +43,7 @@ import { getMockArtifacts, toArtifactRecords } from '../endpoint/lib/artifacts/m import { Manifest } from '../endpoint/lib/artifacts'; import type { NewPackagePolicy, PackagePolicy } from '@kbn/fleet-plugin/common/types/models'; import type { ManifestSchema } from '../../common/endpoint/schema/manifest'; -import type { DeletePackagePoliciesResponse } from '@kbn/fleet-plugin/common'; +import type { PostDeletePackagePoliciesResponse } from '@kbn/fleet-plugin/common'; import { createMockPolicyData } from '../endpoint/services/feature_usage/mocks'; import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../common/endpoint/service/artifacts/constants'; import { ENDPOINT_EVENT_FILTERS_LIST_ID } from '@kbn/securitysolution-list-constants'; @@ -396,7 +396,7 @@ describe('ingest_integration tests ', () => { await callback(deletePackagePolicyMock()); }; - let removedPolicies: DeletePackagePoliciesResponse; + let removedPolicies: PostDeletePackagePoliciesResponse; let policyId: string; let fakeArtifact: ExceptionListSchema; diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts index f383778b764d2d..a2fe485921e6d8 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts @@ -10,7 +10,7 @@ import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import type { PluginStartContract as AlertsStartContract } from '@kbn/alerting-plugin/server'; import type { PostPackagePolicyCreateCallback, - PostPackagePolicyDeleteCallback, + PostPackagePolicyPostDeleteCallback, PutPackagePolicyUpdateCallback, PostPackagePolicyPostCreateCallback, } from '@kbn/fleet-plugin/server'; @@ -190,7 +190,7 @@ export const getPackagePolicyPostCreateCallback = ( export const getPackagePolicyDeleteCallback = ( exceptionsClient: ExceptionListClient | undefined -): PostPackagePolicyDeleteCallback => { +): PostPackagePolicyPostDeleteCallback => { return async (deletePackagePolicy): Promise => { if (!exceptionsClient) { return; diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts index 9f2dbf416dd801..b423c47f6ca652 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/handlers/remove_policy_from_artifacts.ts @@ -8,7 +8,7 @@ import pMap from 'p-map'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; -import type { PostPackagePolicyDeleteCallback } from '@kbn/fleet-plugin/server'; +import type { PostPackagePolicyPostDeleteCallback } from '@kbn/fleet-plugin/server'; import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../../common/endpoint/service/artifacts/constants'; /** @@ -16,7 +16,7 @@ import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../../common/endpoint/service */ export const removePolicyFromArtifacts = async ( exceptionsClient: ExceptionListClient, - policy: Parameters[0][0] + policy: Parameters[0][0] ) => { let page = 1;