diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/install.ts index 5adf1708eb25a..b4b66f561acc9 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/kibana/assets/install.ts @@ -269,7 +269,7 @@ export async function installKibanaAssetsAndReferences({ // This is where the memory consumption is rising up in the first place const kibanaAssets = getKibanaAssets(packageInstallContext); if (installedPkg) { - await deleteKibanaSavedObjectsAssets({ installedPkg, spaceId }); + await deleteKibanaSavedObjectsAssets({ savedObjectsClient, installedPkg, spaceId }); } let installedKibanaAssetsRefs: KibanaAssetReference[] = []; if (!installAsAdditionalSpace) { @@ -344,7 +344,7 @@ export async function deleteKibanaAssetsAndReferencesForSpace({ 'Impossible to delete kibana assets from the space where the package was installed, you must uninstall the package.' ); } - await deleteKibanaSavedObjectsAssets({ installedPkg, spaceId }); + await deleteKibanaSavedObjectsAssets({ savedObjectsClient, installedPkg, spaceId }); await saveKibanaAssetsRefs(savedObjectsClient, pkgName, [], true); } diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts index cf9d953868b6a..4e85a0f673b36 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.test.ts @@ -273,7 +273,8 @@ describe('cleanUpKibanaAssetsStep', () => { expect(mockedDeleteKibanaAssets).toBeCalledWith({ installedObjects: installedKibana, spaceId: 'default', - packageInfo: packageInstallContext.packageInfo, + packageSpecConditions: { kibana: { version: 'x.y.z' } }, + logger: expect.anything(), }); }); @@ -453,7 +454,8 @@ describe('cleanUpUnusedKibanaAssetsStep', () => { expect(mockedDeleteKibanaAssets).toBeCalledWith({ installedObjects: [installedAssets[1]], spaceId: 'default', - packageInfo: packageInstallContext.packageInfo, + packageSpecConditions: { kibana: { version: 'x.y.z' } }, + logger: expect.anything(), }); }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts index 22c785f568402..ba8f71123eb6d 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/install_state_machine/steps/step_install_kibana_assets.ts @@ -82,7 +82,12 @@ export async function cleanUpKibanaAssetsStep(context: InstallContext) { logger.debug('Retry transition - clean up Kibana assets first'); await withPackageSpan('Retry transition - clean up Kibana assets first', async () => { - await deleteKibanaAssets({ installedObjects, spaceId, packageInfo }); + await deleteKibanaAssets({ + installedObjects, + spaceId, + packageSpecConditions: packageInfo?.conditions, + logger, + }); }); } } @@ -124,6 +129,11 @@ export async function cleanUpUnusedKibanaAssetsStep(context: InstallContext) { } await withPackageSpan('Clean up Kibana assets that are no longer in the package', async () => { - await deleteKibanaAssets({ installedObjects: assetsToRemove, spaceId, packageInfo }); + await deleteKibanaAssets({ + installedObjects: assetsToRemove, + spaceId, + packageSpecConditions: packageInfo?.conditions, + logger, + }); }); } diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/remove.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/remove.ts index 815e74c9411e4..70e6f706d5c08 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/remove.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import type { ElasticsearchClient, SavedObjectsClientContract, Logger } from '@kbn/core/server'; +import { chunk } from 'lodash'; import type { SavedObject } from '@kbn/core/server'; @@ -16,7 +17,6 @@ import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; import { SavedObjectsUtils, SavedObjectsErrorHelpers } from '@kbn/core/server'; import minVersion from 'semver/ranges/min-version'; -import { chunk } from 'lodash'; import pMap from 'p-map'; import { updateIndexSettings } from '../elasticsearch/index/update_settings'; @@ -35,8 +35,7 @@ import type { EsAssetReference, KibanaAssetReference, Installation, - ArchivePackage, - RegistryPackage, + InstallSource, } from '../../../types'; import { deletePipeline } from '../elasticsearch/ingest_pipeline'; import { removeUnusedIndexPatterns } from '../kibana/index_pattern/install'; @@ -51,9 +50,10 @@ import { auditLoggingService } from '../../audit_logging'; import { FleetError, PackageRemovalError } from '../../../errors'; import { populatePackagePolicyAssignedAgentsCount } from '../../package_policies/populate_package_policy_assigned_agents_count'; -import * as Registry from '../registry'; -import { getInstallation, kibanaSavedObjectTypes } from '.'; +import type { PackageSpecConditions } from '../../../../common'; + +import { getInstallation, getPackageInfo, kibanaSavedObjectTypes } from '.'; const MAX_ASSETS_TO_DELETE = 1000; @@ -63,6 +63,7 @@ export async function removeInstallation(options: { pkgVersion?: string; esClient: ElasticsearchClient; force?: boolean; + installSource?: InstallSource; }): Promise { const { savedObjectsClient, pkgName, esClient } = options; const installation = await getInstallation({ savedObjectsClient, pkgName }); @@ -99,7 +100,7 @@ export async function removeInstallation(options: { // Delete the installed assets. Don't include installation.package_assets. Those are irrelevant to users const installedAssets = [...installation.installed_kibana, ...installation.installed_es]; - await deleteAssets(installation, esClient); + await deleteAssets(savedObjectsClient, installation, esClient); // Delete the manager saved object with references to the asset objects // could also update with [] or some other state @@ -139,22 +140,26 @@ export async function removeInstallation(options: { */ export async function deleteKibanaAssets({ installedObjects, - packageInfo, + packageSpecConditions, + logger, spaceId = DEFAULT_SPACE_ID, }: { installedObjects: KibanaAssetReference[]; + logger: Logger; + packageSpecConditions?: PackageSpecConditions; spaceId?: string; - packageInfo: RegistryPackage | ArchivePackage; }) { const savedObjectsClient = new SavedObjectsClient( appContextService.getSavedObjects().createInternalRepository() ); const namespace = SavedObjectsUtils.namespaceStringToId(spaceId); + if (namespace) { + logger.debug(`Deleting Kibana assets in namespace: ${namespace}`); + } - // TODO this should be the installed package info, not the package that is being installed - const minKibana = packageInfo.conditions?.kibana?.version - ? minVersion(packageInfo.conditions.kibana.version) + const minKibana = packageSpecConditions?.kibana?.version + ? minVersion(packageSpecConditions.kibana.version) : null; // Compare Kibana versions to determine if the package could been installed @@ -162,7 +167,7 @@ export async function deleteKibanaAssets({ // and delete the assets directly. Otherwise, we need to resolve the assets // which might create high memory pressure if a package has a lot of assets. if (minKibana && minKibana.major >= 8) { - await bulkDeleteSavedObjects(installedObjects, namespace, savedObjectsClient); + await bulkDeleteSavedObjects(installedObjects, namespace, savedObjectsClient, logger); } else { const { resolved_objects: resolvedObjects } = await savedObjectsClient.bulkResolve( installedObjects, @@ -185,23 +190,25 @@ export async function deleteKibanaAssets({ // we filter these out before calling delete const assetsToDelete = foundObjects.map(({ saved_object: { id, type } }) => ({ id, type })); - await bulkDeleteSavedObjects(assetsToDelete, namespace, savedObjectsClient); + await bulkDeleteSavedObjects(assetsToDelete, namespace, savedObjectsClient, logger); } } async function bulkDeleteSavedObjects( assetsToDelete: Array<{ id: string; type: string }>, namespace: string | undefined, - savedObjectsClient: SavedObjectsClientContract + savedObjectsClient: SavedObjectsClientContract, + logger: Logger ) { + logger.debug(`Starting bulk deletion of assets and saved objects`); for (const asset of assetsToDelete) { + logger.debug(`Delete asset - id: ${asset?.id}, type: ${asset?.type},`); auditLoggingService.writeCustomSoAuditLog({ action: 'delete', id: asset.id, savedObjectType: asset.type, }); } - // Delete assets in chunks to avoid high memory pressure. This is mostly // relevant for packages containing many assets, as large payload and response // objects are created in memory during the delete operation. While chunking @@ -328,6 +335,7 @@ export async function deletePrerequisiteAssets( } async function deleteAssets( + savedObjectsClient: SavedObjectsClientContract, { installed_es: installedEs, installed_kibana: installedKibana, @@ -335,6 +343,7 @@ async function deleteAssets( additional_spaces_installed_kibana: installedInAdditionalSpacesKibana = {}, name, version, + install_source: installSource, }: Installation, esClient: ElasticsearchClient ) { @@ -352,17 +361,29 @@ async function deleteAssets( esClient ); - // delete the other asset types try { - const packageInfo = await Registry.fetchInfo(name, version); + const packageInfo = await getPackageInfo({ + savedObjectsClient, + pkgName: name, + pkgVersion: version, + skipArchive: installSource !== 'registry', + }); + + // delete the other asset types await Promise.all([ ...deleteESAssets(otherAssets, esClient), - deleteKibanaAssets({ installedObjects: installedKibana, spaceId, packageInfo }), + deleteKibanaAssets({ + installedObjects: installedKibana, + spaceId, + packageSpecConditions: packageInfo?.conditions, + logger, + }), Object.entries(installedInAdditionalSpacesKibana).map(([additionalSpaceId, kibanaAssets]) => deleteKibanaAssets({ installedObjects: kibanaAssets, spaceId: additionalSpaceId, - packageInfo, + logger, + packageSpecConditions: packageInfo?.conditions, }) ), ]); @@ -397,9 +418,11 @@ async function deleteComponentTemplate(esClient: ElasticsearchClient, name: stri } export async function deleteKibanaSavedObjectsAssets({ + savedObjectsClient, installedPkg, spaceId, }: { + savedObjectsClient: SavedObjectsClientContract; installedPkg: SavedObject; spaceId?: string; }) { @@ -422,15 +445,18 @@ export async function deleteKibanaSavedObjectsAssets({ .map(({ id, type }) => ({ id, type } as KibanaAssetReference)); try { - const packageInfo = await Registry.fetchInfo( - installedPkg.attributes.name, - installedPkg.attributes.version - ); + const packageInfo = await getPackageInfo({ + savedObjectsClient, + pkgName: installedPkg.attributes.name, + pkgVersion: installedPkg.attributes.version, + skipArchive: installedPkg.attributes.install_source !== 'registry', + }); await deleteKibanaAssets({ installedObjects: assetsToDelete, spaceId: spaceIdToDelete, - packageInfo, + packageSpecConditions: packageInfo?.conditions, + logger, }); } catch (err) { // in the rollback case, partial installs are likely, so missing assets are not an error