diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index f5d2606dd7e0c..9ccf60dc80a5f 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -17,7 +17,6 @@ import { BulkInstallPackageInfo, BulkInstallPackagesResponse, IBulkInstallPackageHTTPError, - ASSETS_SAVED_OBJECT_TYPE, } from '../../../common'; import { GetCategoriesRequestSchema, @@ -48,7 +47,7 @@ import { defaultIngestErrorHandler, ingestErrorToResponseOptions } from '../../e import { splitPkgKey } from '../../services/epm/registry'; import { licenseService } from '../../services'; import { getArchiveEntry } from '../../services/epm/archive/cache'; -import { PackageAsset, assetPathToObjectId } from '../../services/epm/archive/save_to_es'; +import { getAsset } from '../../services/epm/archive/storage'; export const getCategoriesHandler: RequestHandler< undefined, @@ -113,45 +112,50 @@ export const getFileHandler: RequestHandler( - ASSETS_SAVED_OBJECT_TYPE, - assetPathToObjectId(archiveKey) - ); + const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; + const fileBuffer = getArchiveEntry(assetPath); + // only pull local installation if we don't have it cached + const storedAsset = !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath })); - if (!archiveEntry && !assetSavedObject) { + // error, if neither is available + if (!fileBuffer && !storedAsset) { return response.custom({ body: `installed package file not found: ${filePath}`, statusCode: 404, }); } - const headerContentType = - assetSavedObject.attributes.media_type || mime.contentType(path.extname(archiveKey)); - if (!headerContentType) { + // if storedAsset is not available, fileBuffer *must* be + // b/c we error if we don't have at least one, and storedAsset is the least likely + const { buffer, contentType } = storedAsset + ? { + contentType: storedAsset.media_type, + buffer: storedAsset.data_utf8 + ? Buffer.from(storedAsset.data_utf8, 'utf8') + : Buffer.from(storedAsset.data_base64, 'base64'), + } + : { + contentType: mime.contentType(path.extname(assetPath)), + buffer: fileBuffer, + }; + + if (!contentType) { return response.custom({ body: `unknown content type for file: ${filePath}`, statusCode: 400, }); } - const { data_base64: base64, data_utf8: utf8 } = assetSavedObject.attributes; - // if we have a local Buffer, use that - // else, create one from the saved object (try utf8 first) - const responseBody = - archiveEntry || utf8 ? Buffer.from(utf8, 'utf8') : Buffer.from(base64, 'base64'); - return response.custom({ - body: responseBody, + body: buffer, statusCode: 200, headers: { 'cache-control': 'max-age=10, public', - 'content-type': headerContentType, + 'content-type': contentType, }, }); } else { - const registryResponse = await getFile(`/package/${pkgName}/${pkgVersion}/${filePath}`); + const registryResponse = await getFile(pkgName, pkgVersion, filePath); const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { const value = registryResponse.headers.get(knownHeader); diff --git a/x-pack/plugins/fleet/server/services/epm/archive/save_to_es.ts b/x-pack/plugins/fleet/server/services/epm/archive/storage.ts similarity index 90% rename from x-pack/plugins/fleet/server/services/epm/archive/save_to_es.ts rename to x-pack/plugins/fleet/server/services/epm/archive/storage.ts index 308747804c986..f7323279e0e7c 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/save_to_es.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/storage.ts @@ -121,3 +121,20 @@ export async function archiveEntryToBulkCreateObject(opts: { attributes: doc, }; } + +export async function getAsset(opts: { + savedObjectsClient: SavedObjectsClientContract; + path: string; +}) { + const { savedObjectsClient, path } = opts; + const assetSavedObject = await savedObjectsClient.get( + ASSETS_SAVED_OBJECT_TYPE, + assetPathToObjectId(path) + ); + const storedAsset = assetSavedObject?.attributes; + if (!storedAsset) { + return; + } + + return storedAsset; +} 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 7b84ecc259a5f..b0a76016e8eee 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 @@ -29,7 +29,7 @@ import { updateCurrentWriteIndices } from '../elasticsearch/template/template'; import { deleteKibanaSavedObjectsAssets } from './remove'; import { installTransform } from '../elasticsearch/transform/install'; import { createInstallation, saveKibanaAssetsRefs, updateVersion } from './install'; -import { saveArchiveEntries } from '../archive/save_to_es'; +import { saveArchiveEntries } from '../archive/storage'; // this is only exported for testing // use a leading underscore to indicate it's not the supported path diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts index 0d7006ca41d2b..01aaf111fef84 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -18,7 +18,7 @@ import * as Registry from '../registry'; import { createInstallableFrom, isRequiredPackage } from './index'; import { getArchivePackage } from '../archive'; -export { fetchFile as getFile, SearchParams } from '../registry'; +export { getFile, SearchParams } from '../registry'; function nameAsTitle(name: string) { return name.charAt(0).toUpperCase() + name.substr(1).toLowerCase(); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts index 6e0d574d311cc..63bf1ed53fb97 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts @@ -23,7 +23,7 @@ import { deleteTransforms } from '../elasticsearch/transform/remove'; import { packagePolicyService, appContextService } from '../..'; import { splitPkgKey } from '../registry'; import { deletePackageCache } from '../archive'; -import { removeArchiveEntries } from '../archive/save_to_es'; +import { removeArchiveEntries } from '../archive/storage'; export async function removeInstallation(options: { savedObjectsClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index d8368f2a46d19..90f9afe2350ea 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -110,6 +110,15 @@ export async function fetchInfo(pkgName: string, pkgVersion: string): Promise { + const filePath = `/package/${pkgName}/${pkgVersion}/${relPath}`; + return fetchFile(filePath); +} + export async function fetchFile(filePath: string): Promise { const registryUrl = getRegistryUrl(); return getResponse(`${registryUrl}${filePath}`);