diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts index c85bfeced9db1..fef8cd1872413 100644 --- a/x-pack/plugins/fleet/server/errors/handlers.ts +++ b/x-pack/plugins/fleet/server/errors/handlers.ts @@ -42,6 +42,7 @@ import { AgentRequestInvalidError, PackagePolicyRequestError, FleetNotFoundError, + PackageSavedObjectConflictError, } from '.'; type IngestErrorHandler = ( @@ -100,6 +101,9 @@ const getHTTPResponseCode = (error: FleetError): number => { if (error instanceof ConcurrentInstallOperationError) { return 409; } + if (error instanceof PackageSavedObjectConflictError) { + return 409; + } if (error instanceof PackagePolicyNameExistsError) { return 409; } diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index ce7245672e623..6a69581c11965 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -43,6 +43,7 @@ export class PackageInvalidArchiveError extends FleetError {} export class PackageRemovalError extends FleetError {} export class PackageESError extends FleetError {} export class ConcurrentInstallOperationError extends FleetError {} +export class PackageSavedObjectConflictError extends FleetError {} export class KibanaSOReferenceError extends FleetError {} export class PackageAlreadyInstalledError extends FleetError {} diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts index d6d653fd98c4e..ce416a6277313 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts @@ -14,7 +14,7 @@ import { savedObjectsClientMock, elasticsearchServiceMock } from '@kbn/core/serv import { loggerMock } from '@kbn/logging-mocks'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; -import { ConcurrentInstallOperationError } from '../../../errors'; +import { ConcurrentInstallOperationError, PackageSavedObjectConflictError } from '../../../errors'; import type { Installation } from '../../../../common'; @@ -254,7 +254,6 @@ describe('_installPackage', () => { }); describe('when package is stuck in `installing`', () => { - afterEach(() => {}); const mockInstalledPackageSo: SavedObject = { id: 'mocked-package', attributes: { @@ -387,4 +386,56 @@ describe('_installPackage', () => { }); }); }); + + it('surfaces saved object conflicts error', () => { + appContextService.start( + createAppContextStartContractMock({ + internal: { + disableILMPolicies: false, + disableProxies: false, + fleetServerStandalone: false, + onlyAllowAgentUpgradeToKnownVersions: false, + retrySetupOnBoot: false, + registry: { + kibanaVersionCheckEnabled: true, + capabilities: [], + excludePackages: [], + }, + }, + }) + ); + + mockedInstallKibanaAssetsAndReferences.mockRejectedValueOnce( + new PackageSavedObjectConflictError('test') + ); + + expect( + _installPackage({ + savedObjectsClient: soClient, + // @ts-ignore + savedObjectsImporter: jest.fn(), + esClient, + logger: loggerMock.create(), + packageInstallContext: { + packageInfo: { + title: 'title', + name: 'xyz', + version: '4.5.6', + description: 'test', + type: 'integration', + categories: ['cloud', 'custom'], + format_version: 'string', + release: 'experimental', + conditions: { kibana: { version: 'x.y.z' } }, + owner: { github: 'elastic/fleet' }, + } as any, + assetsMap: new Map(), + paths: [], + }, + installType: 'install', + installSource: 'registry', + spaceId: DEFAULT_SPACE_ID, + }) + ).rejects.toThrowError(PackageSavedObjectConflictError); + }); }); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index 6c30d3a8d332d..e182fd8721075 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -45,7 +45,7 @@ import { installTransforms } from '../elasticsearch/transform/install'; import { installMlModel } from '../elasticsearch/ml_model'; import { installIlmForDataStream } from '../elasticsearch/datastream_ilm/install'; import { saveArchiveEntriesFromAssetsMap } from '../archive/storage'; -import { ConcurrentInstallOperationError } from '../../../errors'; +import { ConcurrentInstallOperationError, PackageSavedObjectConflictError } from '../../../errors'; import { appContextService, packagePolicyService } from '../..'; import { auditLoggingService } from '../../audit_logging'; @@ -387,10 +387,12 @@ export async function _installPackage({ return [...installedKibanaAssetsRefs, ...esReferences]; } catch (err) { if (SavedObjectsErrorHelpers.isConflictError(err)) { - throw new ConcurrentInstallOperationError( - `Concurrent installation or upgrade of ${pkgName || 'unknown'}-${ + throw new PackageSavedObjectConflictError( + `Saved Object conflict encountered while installing ${pkgName || 'unknown'}-${ pkgVersion || 'unknown' - } detected, aborting. Original error: ${err.message}` + }. There may be a conflicting Saved Object saved to another Space. Original error: ${ + err.message + }` ); } else { throw err;