diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts index 4007ad7545ece..3eb689dfa1a2a 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts @@ -10,6 +10,7 @@ import type { PackageClient, PackageService } from './package_service'; const createClientMock = (): jest.Mocked => ({ getInstallation: jest.fn(), ensureInstalledPackage: jest.fn(), + installPackage: jest.fn(), fetchFindLatestPackage: jest.fn(), readBundledPackage: jest.fn(), getPackage: jest.fn(), diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.ts b/x-pack/plugins/fleet/server/services/epm/package_service.ts index eee1afb37dcaa..a535af9636d1a 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -8,34 +8,40 @@ /* eslint-disable max-classes-per-file */ import type { - KibanaRequest, ElasticsearchClient, - SavedObjectsClientContract, + KibanaRequest, Logger, + SavedObjectsClientContract, } from '@kbn/core/server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; + import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header'; import type { PackageList } from '../../../common'; import type { + ArchivePackage, + BundledPackage, CategoryId, EsAssetReference, InstallablePackage, Installation, RegistryPackage, - ArchivePackage, - BundledPackage, } from '../../types'; import type { FleetAuthzRouteConfig } from '../security/types'; -import { checkSuperuser, getAuthzFromRequest, doesNotHaveRequiredFleetAuthz } from '../security'; -import { FleetUnauthorizedError, FleetError } from '../../errors'; +import { checkSuperuser, doesNotHaveRequiredFleetAuthz, getAuthzFromRequest } from '../security'; +import { FleetError, FleetUnauthorizedError } from '../../errors'; import { INSTALL_PACKAGES_AUTHZ, READ_PACKAGE_INFO_AUTHZ } from '../../routes/epm'; -import { installTransforms, isTransform } from './elasticsearch/transform/install'; +import type { InstallResult } from '../../../common'; + import type { FetchFindLatestPackageOptions } from './registry'; +import * as Registry from './registry'; import { fetchFindLatestPackageOrThrow, getPackage } from './registry'; -import { ensureInstalledPackage, getInstallation, getPackages } from './packages'; + +import { installTransforms, isTransform } from './elasticsearch/transform/install'; +import { ensureInstalledPackage, getInstallation, getPackages, installPackage } from './packages'; import { generatePackageInfoFromArchiveBuffer } from './archive'; export type InstalledAssetType = EsAssetReference; @@ -52,8 +58,16 @@ export interface PackageClient { pkgName: string; pkgVersion?: string; spaceId?: string; + force?: boolean; }): Promise; + installPackage(options: { + pkgName: string; + pkgVersion?: string; + spaceId?: string; + force?: boolean; + }): Promise; + fetchFindLatestPackage( packageName: string, options?: FetchFindLatestPackageOptions @@ -151,6 +165,7 @@ class PackageClientImpl implements PackageClient { pkgName: string; pkgVersion?: string; spaceId?: string; + force?: boolean; }): Promise { await this.#runPreflight(INSTALL_PACKAGES_AUTHZ); @@ -160,6 +175,32 @@ class PackageClientImpl implements PackageClient { savedObjectsClient: this.internalSoClient, }); } + public async installPackage(options: { + pkgName: string; + pkgVersion?: string; + spaceId?: string; + force?: boolean; + }): Promise { + await this.#runPreflight(INSTALL_PACKAGES_AUTHZ); + + const { pkgName, pkgVersion, spaceId = DEFAULT_SPACE_ID, force = false } = options; + + // If pkgVersion isn't specified, find the latest package version + const pkgKeyProps = pkgVersion + ? { name: pkgName, version: pkgVersion } + : await Registry.fetchFindLatestPackageOrThrow(pkgName, { prerelease: true }); + const pkgkey = Registry.pkgToPkgKey(pkgKeyProps); + + return await installPackage({ + force, + pkgkey, + spaceId, + installSource: 'registry', + esClient: this.internalEsClient, + savedObjectsClient: this.internalSoClient, + neverIgnoreVerificationError: !force, + }); + } public async fetchFindLatestPackage( packageName: string,