Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] Rely on package content during installation #142353

Merged
merged 15 commits into from
Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from 14 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 @@ -20,7 +20,7 @@ export async function getLatestApmPackage({
const { name, version } = await packageClient.fetchFindLatestPackage(
APM_PACKAGE_NAME
);
const registryPackage = await packageClient.getRegistryPackage(name, version);
const registryPackage = await packageClient.getPackage(name, version);
const { title, policy_templates: policyTemplates } =
registryPackage.packageInfo;
const firstTemplate = policyTemplates?.[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('validate bundled packages', () => {
bundledPackages
.filter((pkg) => !EXCLUDED_PACKAGES.includes(pkg.name))
.map(async (bundledPackage) => {
const registryPackage = await Registry.getRegistryPackage(
const registryPackage = await Registry.getPackage(
bundledPackage.name,
bundledPackage.version
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const createClientMock = (): jest.Mocked<PackageClient> => ({
getInstallation: jest.fn(),
ensureInstalledPackage: jest.fn(),
fetchFindLatestPackage: jest.fn(),
getRegistryPackage: jest.fn(),
getPackage: jest.fn(),
reinstallEsAssets: jest.fn(),
});

Expand Down
10 changes: 5 additions & 5 deletions x-pack/plugins/fleet/server/services/epm/package_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const testKeys = [
'getInstallation',
'ensureInstalledPackage',
'fetchFindLatestPackage',
'getRegistryPackage',
'getPackage',
'reinstallEsAssets',
];

Expand Down Expand Up @@ -99,16 +99,16 @@ function getTest(
break;
case testKeys[3]:
test = {
method: mocks.packageClient.getRegistryPackage.bind(mocks.packageClient),
method: mocks.packageClient.getPackage.bind(mocks.packageClient),
args: ['package name', '8.0.0'],
spy: jest.spyOn(epmRegistry, 'getRegistryPackage'),
spy: jest.spyOn(epmRegistry, 'getPackage'),
spyArgs: ['package name', '8.0.0', undefined],
spyResponse: {
packageInfo: { name: 'getRegistryPackage test' },
packageInfo: { name: 'getPackage test' },
paths: ['/some/test/path'],
},
expectedReturnValue: {
packageInfo: { name: 'getRegistryPackage test' },
packageInfo: { name: 'getPackage test' },
paths: ['/some/test/path'],
},
};
Expand Down
13 changes: 7 additions & 6 deletions x-pack/plugins/fleet/server/services/epm/package_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import type {
InstallablePackage,
Installation,
RegistryPackage,
ArchivePackage,
BundledPackage,
} from '../../types';
import { checkSuperuser } from '../../routes/security';
import { FleetUnauthorizedError } from '../../errors';

import { installTransforms, isTransform } from './elasticsearch/transform/install';
import { fetchFindLatestPackageOrThrow, getRegistryPackage } from './registry';
import { fetchFindLatestPackageOrThrow, getPackage } from './registry';
import { ensureInstalledPackage, getInstallation } from './packages';

export type InstalledAssetType = EsAssetReference;
Expand All @@ -46,10 +47,10 @@ export interface PackageClient {

fetchFindLatestPackage(packageName: string): Promise<RegistryPackage | BundledPackage>;

getRegistryPackage(
getPackage(
packageName: string,
packageVersion: string
): Promise<{ packageInfo: RegistryPackage; paths: string[] }>;
): Promise<{ packageInfo: ArchivePackage; paths: string[] }>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems weird to me that getRegistryPackage return an ArchivePackage should this be a new method, or should rename it getPackage ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calling it getPackage would be a good option. I've been in doubt about the naming because the whole flow is in Registry but it's not going to use the registry anymore.. I guess we'll have to refactor further

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the name to GetPackage, I will need additional approvals from other teams that also use that method.


reinstallEsAssets(
packageInfo: InstallablePackage,
Expand Down Expand Up @@ -120,13 +121,13 @@ class PackageClientImpl implements PackageClient {
return fetchFindLatestPackageOrThrow(packageName);
}

public async getRegistryPackage(
public async getPackage(
packageName: string,
packageVersion: string,
options?: Parameters<typeof getRegistryPackage>['2']
options?: Parameters<typeof getPackage>['2']
) {
await this.#runPreflight();
return getRegistryPackage(packageName, packageVersion, options);
return getPackage(packageName, packageVersion, options);
}

public async reinstallEsAssets(
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/fleet/server/services/epm/packages/get.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ describe('When using EPM `get` services', () => {
name: 'my-package',
version: '1.0.0',
} as RegistryPackage);
MockRegistry.getRegistryPackage.mockResolvedValue({
MockRegistry.getPackage.mockResolvedValue({
paths: [],
packageInfo: {
name: 'my-package',
Expand Down Expand Up @@ -366,7 +366,7 @@ describe('When using EPM `get` services', () => {
status: 'not_installed',
});

expect(MockRegistry.getRegistryPackage).not.toHaveBeenCalled();
expect(MockRegistry.getPackage).not.toHaveBeenCalled();
});

// when calling the get package endpoint without a package version we
Expand All @@ -392,7 +392,7 @@ describe('When using EPM `get` services', () => {
status: 'not_installed',
});

expect(MockRegistry.getRegistryPackage).not.toHaveBeenCalled();
expect(MockRegistry.getPackage).not.toHaveBeenCalled();
});
});
});
Expand Down
20 changes: 7 additions & 13 deletions x-pack/plugins/fleet/server/services/epm/packages/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ export async function getPackageInfo({
pkgVersion: resolvedPkgVersion,
savedObjectsClient,
installedPkg: savedObject?.attributes,
getPkgInfoFromArchive: packageInfo?.type === 'input',
ignoreUnverified,
}));
}
Expand Down Expand Up @@ -235,13 +234,12 @@ interface PackageResponse {
}
type GetPackageResponse = PackageResponse | undefined;

// gets package from install_source if it exists otherwise gets from registry
// gets package from install_source
export async function getPackageFromSource(options: {
pkgName: string;
pkgVersion: string;
installedPkg?: Installation;
savedObjectsClient: SavedObjectsClientContract;
getPkgInfoFromArchive?: boolean;
ignoreUnverified?: boolean;
}): Promise<PackageResponse> {
const logger = appContextService.getLogger();
Expand All @@ -250,7 +248,6 @@ export async function getPackageFromSource(options: {
pkgVersion,
installedPkg,
savedObjectsClient,
getPkgInfoFromArchive = true,
ignoreUnverified = false,
} = options;
let res: GetPackageResponse;
Expand Down Expand Up @@ -280,11 +277,12 @@ export async function getPackageFromSource(options: {
logger.debug(`retrieved installed package ${pkgName}-${pkgVersion} from ES`);
}
}
// for packages not in cache or package storage and installed from registry, check registry
// install source is now archive in all cases
// See https://github.com/elastic/kibana/issues/115032
if (!res && pkgInstallSource === 'registry') {
try {
res = await Registry.getRegistryPackage(pkgName, pkgVersion);
logger.debug(`retrieved installed package ${pkgName}-${pkgVersion} from registry`);
res = await Registry.getPackage(pkgName, pkgVersion);
logger.debug(`retrieved installed package ${pkgName}-${pkgVersion}`);
} catch (error) {
if (error instanceof PackageFailedVerificationError) {
throw error;
Expand All @@ -294,12 +292,8 @@ export async function getPackageFromSource(options: {
}
}
} else {
// else package is not installed or installed and missing from cache and storage and installed from registry
res = await Registry.getRegistryPackage(pkgName, pkgVersion, {
getPkgInfoFromArchive,
ignoreUnverified,
});
logger.debug(`retrieved uninstalled package ${pkgName}-${pkgVersion} from registry`);
res = await Registry.getPackage(pkgName, pkgVersion, { ignoreUnverified });
logger.debug(`retrieved uninstalled package ${pkgName}-${pkgVersion}`);
}
if (!res) {
throw new FleetError(`package info for ${pkgName}-${pkgVersion} does not exist`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('install', () => {
.spyOn(Registry, 'fetchFindLatestPackageOrThrow')
.mockImplementation(() => Promise.resolve({ version: '1.3.0' } as any));
jest
.spyOn(Registry, 'getRegistryPackage')
.spyOn(Registry, 'getPackage')
.mockImplementation(() => Promise.resolve({ packageInfo: { license: 'basic' } } as any));

mockGetBundledPackages.mockReset();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ async function installPackageFromRegistry({
Registry.fetchFindLatestPackageOrThrow(pkgName, {
ignoreConstraints,
}),
Registry.getRegistryPackage(pkgName, pkgVersion, {
Registry.getPackage(pkgName, pkgVersion, {
ignoreUnverified: force && !neverIgnoreVerificationError,
}),
]);
Expand Down
77 changes: 49 additions & 28 deletions x-pack/plugins/fleet/server/services/epm/registry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,35 +250,59 @@ export async function getInfo(name: string, version: string) {
});
}

export async function getRegistryPackage(
// Check that the packageInfo exists in cache
// If not, retrieve it from the archive
async function getPackageInfoFromArchiveOrCache(
name: string,
version: string,
options?: { ignoreUnverified?: boolean; getPkgInfoFromArchive?: boolean }
archiveBuffer: Buffer,
archivePath: string
): Promise<ArchivePackage> {
const cachedInfo = getPackageInfo({ name, version });

if (!cachedInfo) {
const { packageInfo } = await generatePackageInfoFromArchiveBuffer(
archiveBuffer,
ensureContentType(archivePath)
);
setPackageInfo({ packageInfo, name, version });
return packageInfo;
} else {
return cachedInfo;
}
}

export async function getPackage(
name: string,
version: string,
options?: { ignoreUnverified?: boolean }
): Promise<{
paths: string[];
packageInfo: RegistryPackage;
packageInfo: ArchivePackage;
verificationResult?: PackageVerificationResult;
}> {
const verifyPackage = appContextService.getExperimentalFeatures().packageVerification;
let paths = getArchiveFilelist({ name, version });
let verificationResult = verifyPackage ? getVerificationResult({ name, version }) : undefined;

const {
archiveBuffer,
archivePath,
verificationResult: latestVerificationResult,
} = await withPackageSpan('Fetch package archive from archive buffer', () =>
fetchArchiveBuffer({
pkgName: name,
pkgVersion: version,
shouldVerify: verifyPackage,
ignoreUnverified: options?.ignoreUnverified,
})
);

if (latestVerificationResult) {
verificationResult = latestVerificationResult;
setVerificationResult({ name, version }, latestVerificationResult);
}
if (!paths || paths.length === 0) {
const {
archiveBuffer,
archivePath,
verificationResult: latestVerificationResult,
} = await withPackageSpan('Fetch package archive from registry', () =>
fetchArchiveBuffer({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function internally calls getInfo to retrieve the ArchiveBuffer:

export async function fetchArchiveBuffer({
pkgName,
pkgVersion,
shouldVerify,
ignoreUnverified = false,
}: {
pkgName: string;
pkgVersion: string;
shouldVerify: boolean;
ignoreUnverified?: boolean;
}): Promise<{
archiveBuffer: Buffer;
archivePath: string;
verificationResult?: PackageVerificationResult;
}> {
const logger = appContextService.getLogger();
const { download: archivePath } = await getInfo(pkgName, pkgVersion);
const archiveUrl = `${getRegistryUrl()}${archivePath}`;
const archiveBuffer = await getResponseStream(archiveUrl).then(streamToBuffer);

I wonder if I should get the ArchiveBuffer from the cache instead and remove the calls to getInfo altogether.

pkgName: name,
pkgVersion: version,
shouldVerify: verifyPackage,
ignoreUnverified: options?.ignoreUnverified,
})
);
if (latestVerificationResult) {
verificationResult = latestVerificationResult;
setVerificationResult({ name, version }, latestVerificationResult);
}
paths = await withPackageSpan('Unpack archive', () =>
unpackBufferToCache({
name,
Expand All @@ -287,17 +311,14 @@ export async function getRegistryPackage(
contentType: ensureContentType(archivePath),
})
);
const cachedInfo = getPackageInfo({ name, version });
if (options?.getPkgInfoFromArchive && !cachedInfo) {
const { packageInfo } = await generatePackageInfoFromArchiveBuffer(
archiveBuffer,
ensureContentType(archivePath)
);
setPackageInfo({ packageInfo, name, version });
}
}

const packageInfo = await getInfo(name, version);
const packageInfo = await getPackageInfoFromArchiveOrCache(
name,
version,
archiveBuffer,
archivePath
);
return { paths, packageInfo, verificationResult };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ describe('check metadata transforms task', () => {
});

describe('transforms reinstall', () => {
let getRegistryPackageSpy: jest.SpyInstance;
let getPackageSpy: jest.SpyInstance;
let reinstallEsAssetsSpy: jest.SpyInstance;
let mockPackageClient: jest.Mocked<PackageClient>;

Expand All @@ -291,7 +291,7 @@ describe('check metadata transforms task', () => {

mockPackageClient = mockEndpointAppContext.service.getInternalFleetServices()
.packages as jest.Mocked<PackageClient>;
getRegistryPackageSpy = jest.spyOn(mockPackageClient, 'getRegistryPackage');
getPackageSpy = jest.spyOn(mockPackageClient, 'getPackage');
reinstallEsAssetsSpy = jest.spyOn(mockPackageClient, 'reinstallEsAssets');

const transformStatsResponseMock = {
Expand All @@ -313,7 +313,7 @@ describe('check metadata transforms task', () => {
packageInfo: { name: 'package name' },
paths: ['some/test/transform/path'],
};
getRegistryPackageSpy.mockResolvedValue(expectedArgs);
getPackageSpy.mockResolvedValue(expectedArgs);
reinstallEsAssetsSpy.mockResolvedValue([{}]);
await runTask();

Expand All @@ -338,7 +338,7 @@ describe('check metadata transforms task', () => {
});

it('should return correct runAt', async () => {
getRegistryPackageSpy.mockResolvedValue({
getPackageSpy.mockResolvedValue({
packageInfo: { name: 'package name' },
paths: ['some/test/transform/path'],
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ export class CheckMetadataTransformsTask {

const packageClient = this.endpointAppContext.service.getInternalFleetServices().packages;

const { packageInfo, paths } = await packageClient.getRegistryPackage(
const { packageInfo, paths } = await packageClient.getPackage(
FLEET_ENDPOINT_PACKAGE,
pkgVersion
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const getInstalledIntegrationsRoute = (
concurrency: MAX_CONCURRENT_REQUESTS_TO_PACKAGE_REGISTRY,
items: set.getPackages(),
executor: async (packageInfo) => {
const registryPackage = await fleet.packages.getRegistryPackage(
const registryPackage = await fleet.packages.getPackage(
packageInfo.package_name,
packageInfo.package_version
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { capitalize, flatten } from 'lodash';
import type { PackagePolicy, RegistryPackage } from '@kbn/fleet-plugin/common';
import type { PackagePolicy, ArchivePackage } from '@kbn/fleet-plugin/common';
import type {
InstalledIntegration,
InstalledIntegrationArray,
Expand All @@ -18,7 +18,7 @@ import type {

export interface IInstalledIntegrationSet {
addPackagePolicy(policy: PackagePolicy): void;
addRegistryPackage(registryPackage: RegistryPackage): void;
addRegistryPackage(registryPackage: ArchivePackage): void;

getPackages(): InstalledPackageArray;
getIntegrations(): InstalledIntegrationArray;
Expand Down Expand Up @@ -56,7 +56,7 @@ export const createInstalledIntegrationSet = (): IInstalledIntegrationSet => {
}
};

const addRegistryPackage = (registryPackage: RegistryPackage): void => {
const addRegistryPackage = (registryPackage: ArchivePackage): void => {
const policyTemplates = registryPackage.policy_templates ?? [];
const packageKey = `${registryPackage.name}:${registryPackage.version}`;
const existingPackageInfo = packageMap.get(packageKey);
Expand Down
Loading