Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
});
});

Expand Down Expand Up @@ -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(),
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
});
}
}
Expand Down Expand Up @@ -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,
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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';
Expand All @@ -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';
Expand All @@ -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;

Expand All @@ -63,6 +63,7 @@ export async function removeInstallation(options: {
pkgVersion?: string;
esClient: ElasticsearchClient;
force?: boolean;
installSource?: InstallSource;
}): Promise<AssetReference[]> {
const { savedObjectsClient, pkgName, esClient } = options;
const installation = await getInstallation({ savedObjectsClient, pkgName });
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -139,30 +140,34 @@ 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
// only in 8.x or later. If so, we can skip SO resolution step altogether
// 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,
Expand All @@ -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
Expand Down Expand Up @@ -328,13 +335,15 @@ export async function deletePrerequisiteAssets(
}

async function deleteAssets(
savedObjectsClient: SavedObjectsClientContract,
{
installed_es: installedEs,
installed_kibana: installedKibana,
installed_kibana_space_id: spaceId = DEFAULT_SPACE_ID,
additional_spaces_installed_kibana: installedInAdditionalSpacesKibana = {},
name,
version,
install_source: installSource,
}: Installation,
esClient: ElasticsearchClient
) {
Expand All @@ -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,
})
),
]);
Expand Down Expand Up @@ -397,9 +418,11 @@ async function deleteComponentTemplate(esClient: ElasticsearchClient, name: stri
}

export async function deleteKibanaSavedObjectsAssets({
savedObjectsClient,
installedPkg,
spaceId,
}: {
savedObjectsClient: SavedObjectsClientContract;
installedPkg: SavedObject<Installation>;
spaceId?: string;
}) {
Expand All @@ -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
Expand Down
Loading