From 2b5df12615d23ed2d54ab889a3964d42a13622be Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Tue, 7 Feb 2023 10:10:28 +0100 Subject: [PATCH] [Fleet] return uploaded packages that are not in registry (#150332) ## Summary Related to https://github.com/elastic/kibana/issues/148599 Fixes https://github.com/elastic/kibana/issues/81995 Changed `GET /epm/packages` API to list uploaded packages as well that are not in registry. Testing steps: - Built a test package that is not in registry from kibana repo: ``` x-pack/test/fleet_api_integration/apis/fixtures/package_verification/packages/src/verified-1.0.0 elastic-package build --zip -v ``` - Uploaded the resulting zip [verified-1.0.0.zip](https://github.com/elastic/kibana/files/10664094/verified-1.0.0.zip) ``` curl -XPOST -H 'content-type: application/zip' -H 'kbn-xsrf: true' http://localhost:5601/julia/api/fleet/epm/packages -u elastic:changeme --data-binary @verified-1.0.0.zip ``` - Navigate to `Integrations` page, verify that the `Verified` package is displayed under `Browse` / `Installed` integrations, and the details are visible when clicking on the package. image image image ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../server/services/epm/packages/get.test.ts | 61 ++++++++++++++++++- .../fleet/server/services/epm/packages/get.ts | 12 +++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts index 19ced885822a4f7..52170f6c302fdfa 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts @@ -24,7 +24,7 @@ import { PackageNotFoundError } from '../../../errors'; import { getSettings } from '../../settings'; -import { getPackageInfo, getPackageUsageStats } from './get'; +import { getPackageInfo, getPackages, getPackageUsageStats } from './get'; const MockRegistry = Registry as jest.Mocked; @@ -186,6 +186,65 @@ describe('When using EPM `get` services', () => { }); }); + describe('getPackages', () => { + beforeEach(() => { + const mockContract = createAppContextStartContractMock(); + appContextService.start(mockContract); + jest.clearAllMocks(); + MockRegistry.fetchList.mockResolvedValue([ + { + name: 'nginx', + version: '1.0.0', + title: 'Nginx', + } as any, + ]); + }); + + it('should return installed package that is not in registry', async () => { + const soClient = savedObjectsClientMock.create(); + soClient.find.mockResolvedValue({ + saved_objects: [ + { + id: 'elasticsearch', + attributes: { + name: 'elasticsearch', + version: '0.0.1', + install_status: 'upload', + }, + }, + ], + } as any); + + await expect( + getPackages({ + savedObjectsClient: soClient, + }) + ).resolves.toMatchObject([ + { + name: 'elasticsearch', + version: '0.0.1', + title: 'Elasticsearch', + status: 'upload', + savedObject: { + id: 'elasticsearch', + attributes: { + name: 'elasticsearch', + version: '0.0.1', + install_status: 'upload', + }, + }, + }, + { + name: 'nginx', + version: '1.0.0', + title: 'Nginx', + id: 'nginx', + status: 'not_installed', + }, + ]); + }); + }); + describe('getPackageInfo', () => { beforeEach(() => { const mockContract = createAppContextStartContractMock(); 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 8776ca6014e55be..1d048e2cf2d2cde 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -14,7 +14,11 @@ import { PACKAGE_POLICY_SAVED_OBJECT_TYPE, } from '../../../../common/constants'; import { isPackageLimited } from '../../../../common/services'; -import type { PackageUsageStats, PackagePolicySOAttributes } from '../../../../common/types'; +import type { + PackageUsageStats, + PackagePolicySOAttributes, + Installable, +} from '../../../../common/types'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; import type { ArchivePackage, @@ -68,6 +72,11 @@ export async function getPackages( }); // get the installed packages const packageSavedObjects = await getPackageSavedObjects(savedObjectsClient); + + const packagesNotInRegistry = packageSavedObjects.saved_objects + .filter((pkg) => !registryItems.some((item) => item.name === pkg.id)) + .map((pkg) => createInstallableFrom({ ...pkg.attributes, title: nameAsTitle(pkg.id) }, pkg)); + const packageList = registryItems .map((item) => createInstallableFrom( @@ -75,6 +84,7 @@ export async function getPackages( packageSavedObjects.saved_objects.find(({ id }) => id === item.name) ) ) + .concat(packagesNotInRegistry as Installable) .sort(sortByName); if (!excludeInstallStatus) {