[Fleet] Support package capabilities filtering (#162435)

This commit is contained in:
Nicolas Chaulet 2023-07-28 15:58:06 -04:00 committed by GitHub
parent 1dbd736b15
commit 9b8950254e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 100 additions and 16 deletions

View file

@ -26,3 +26,6 @@ telemetry.labels.serverless: search
# Alerts config
xpack.actions.enabledActionTypes: ['.email', '.index', '.slack', '.jira', '.webhook', '.teams']
# Fleet specific configuration
xpack.fleet.internal.capabilities: ['serverless_search']

View file

@ -24,6 +24,9 @@ xpack.fleet.agentIdVerificationEnabled: false
## APM Serverless Onboarding flow
xpack.apm.serverlessOnboarding: true
# Fleet specific configuration
xpack.fleet.internal.capabilities: ['apm', 'uptime', 'observability']
## Required for force installation of APM Package
xpack.fleet.packages:
- name: apm

View file

@ -23,7 +23,9 @@ xpack.serverless.plugin.developer.projectSwitcher.currentType: 'security'
# Specify in telemetry the project type
telemetry.labels.serverless: security
# Fleet specific configuration
xpack.fleet.internal.capabilities: ['security']
# Serverless security specific options
xpack.securitySolution.enableExperimental:
- discoverInTimeline
- discoverInTimeline

View file

@ -50,6 +50,7 @@ export interface FleetConfigType {
disableProxies: boolean;
fleetServerStandalone: boolean;
activeAgentsSoftLimit?: number;
capabilities: string[];
};
createArtifactsBulkBatchSize?: number;
}

View file

@ -182,6 +182,18 @@ export const config: PluginConfigDescriptor = {
min: 0,
})
),
capabilities: schema.arrayOf(
schema.oneOf([
// See package-spec for the list of available capiblities https://github.com/elastic/package-spec/blob/dcc37b652690f8a2bca9cf8a12fc28fd015730a0/spec/integration/manifest.spec.yml#L113
schema.literal('apm'),
schema.literal('enterprise_search'),
schema.literal('observability'),
schema.literal('security'),
schema.literal('serverless_search'),
schema.literal('uptime'),
]),
{ defaultValue: [] }
),
})
),
enabled: schema.boolean({ defaultValue: true }),

View file

@ -124,6 +124,7 @@ describe('_installPackage', () => {
disableILMPolicies: true,
disableProxies: false,
fleetServerStandalone: false,
capabilities: [],
},
})
);
@ -176,6 +177,7 @@ describe('_installPackage', () => {
disableProxies: false,
disableILMPolicies: false,
fleetServerStandalone: false,
capabilities: [],
},
})
);

View file

@ -40,6 +40,7 @@ import type {
RegistryPackage,
EpmPackageAdditions,
GetCategoriesRequest,
GetPackagesRequest,
} from '../../../../common/types';
import type { Installation, PackageInfo, PackagePolicySOAttributes } from '../../../types';
import {
@ -61,7 +62,6 @@ import { getFilteredSearchPackages } from '../filtered_packages';
import { createInstallableFrom } from '.';
export type { SearchParams } from '../registry';
export { getFile } from '../registry';
function nameAsTitle(name: string) {
@ -76,7 +76,7 @@ export async function getPackages(
options: {
savedObjectsClient: SavedObjectsClientContract;
excludeInstallStatus?: boolean;
} & Registry.SearchParams
} & GetPackagesRequest['query']
) {
const {
savedObjectsClient,

View file

@ -13,7 +13,6 @@ import { KibanaAssetType } from '../../../types';
import type { AssetType, Installable, Installation } from '../../../types';
export { bulkInstallPackages, isBulkInstallError } from './bulk_install_packages';
export type { SearchParams } from './get';
export {
getCategories,
getFile,

View file

@ -8,7 +8,6 @@
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { PackageNotFoundError, RegistryResponseError } from '../../../errors';
import * as Archive from '../archive';
import {
@ -17,10 +16,13 @@ import {
fetchFindLatestPackageOrThrow,
fetchInfo,
getLicensePath,
fetchCategories,
fetchList,
} from '.';
const mockLoggerFactory = loggingSystemMock.create();
const mockLogger = mockLoggerFactory.get('mock logger');
const mockGetConfig = jest.fn();
const mockGetBundledPackageByName = jest.fn();
const mockFetchUrl = jest.fn();
@ -34,7 +36,7 @@ jest.mock('../..', () => ({
getLogger: () => mockLogger,
getKibanaBranch: () => 'main',
getKibanaVersion: () => '99.0.0',
getConfig: () => ({}),
getConfig: () => mockGetConfig(),
getIsProductionMode: () => false,
},
}));
@ -219,3 +221,57 @@ describe('fetchInfo', () => {
}
});
});
describe('fetchCategories', () => {
beforeEach(() => {
mockFetchUrl.mockReset();
mockGetConfig.mockReset();
});
it('call registry with capabilities if configured', async () => {
mockGetConfig.mockReturnValue({
internal: {
capabilities: ['apm', 'security'],
},
});
mockFetchUrl.mockResolvedValue(JSON.stringify([]));
await fetchCategories();
expect(mockFetchUrl).toBeCalledTimes(1);
const callUrl = new URL(mockFetchUrl.mock.calls[0][0]);
expect(callUrl.searchParams.get('capabilities')).toBe('apm,security');
});
it('does not call registry with capabilities if none are configured', async () => {
mockGetConfig.mockReturnValue({});
mockFetchUrl.mockResolvedValue(JSON.stringify([]));
await fetchCategories();
expect(mockFetchUrl).toBeCalledTimes(1);
const callUrl = new URL(mockFetchUrl.mock.calls[0][0]);
expect(callUrl.searchParams.get('capabilities')).toBeNull();
});
});
describe('fetchList', () => {
beforeEach(() => {
mockFetchUrl.mockReset();
mockGetConfig.mockReset();
});
it('call registry with capabilities if configured', async () => {
mockGetConfig.mockReturnValue({
internal: {
capabilities: ['apm', 'security'],
},
});
mockFetchUrl.mockResolvedValue(JSON.stringify([]));
await fetchList();
expect(mockFetchUrl).toBeCalledTimes(1);
const callUrl = new URL(mockFetchUrl.mock.calls[0][0]);
expect(callUrl.searchParams.get('capabilities')).toBe('apm,security');
});
it('does not call registry with capabilities if none are configured', async () => {
mockGetConfig.mockReturnValue({});
mockFetchUrl.mockResolvedValue(JSON.stringify([]));
await fetchList();
expect(mockFetchUrl).toBeCalledTimes(1);
const callUrl = new URL(mockFetchUrl.mock.calls[0][0]);
expect(callUrl.searchParams.get('capabilities')).toBeNull();
});
});

View file

@ -18,11 +18,11 @@ import { splitPkgKey as split } from '../../../../common/services';
import { KibanaAssetType } from '../../../types';
import type {
AssetsGroupedByServiceByType,
CategoryId,
CategorySummaryList,
RegistryPackage,
RegistrySearchResults,
GetCategoriesRequest,
GetPackagesRequest,
PackageVerificationResult,
ArchivePackage,
BundledPackage,
@ -54,19 +54,14 @@ import { verifyPackageArchiveSignature } from '../packages/package_verification'
import { fetchUrl, getResponse, getResponseStream } from './requests';
import { getRegistryUrl } from './registry_url';
export interface SearchParams {
category?: CategoryId;
prerelease?: boolean;
// deprecated
experimental?: boolean;
}
export const splitPkgKey = split;
export const pkgToPkgKey = ({ name, version }: { name: string; version: string }) =>
`${name}-${version}`;
export async function fetchList(params?: SearchParams): Promise<RegistrySearchResults> {
export async function fetchList(
params?: GetPackagesRequest['query']
): Promise<RegistrySearchResults> {
const registryUrl = getRegistryUrl();
const url = new URL(`${registryUrl}/search`);
if (params) {
@ -79,6 +74,7 @@ export async function fetchList(params?: SearchParams): Promise<RegistrySearchRe
}
setKibanaVersion(url);
setCapabilities(url);
return fetchUrl(url.toString()).then(JSON.parse);
}
@ -111,6 +107,7 @@ async function _fetchFindLatestPackage(
if (!ignoreConstraints) {
setKibanaVersion(url);
setCapabilities(url);
}
try {
@ -239,6 +236,13 @@ function setKibanaVersion(url: URL) {
}
}
function setCapabilities(url: URL) {
const capabilities = appContextService.getConfig()?.internal?.capabilities;
if (capabilities && capabilities.length > 0) {
url.searchParams.set('capabilities', capabilities.join(','));
}
}
export async function fetchCategories(
params?: GetCategoriesRequest['query']
): Promise<CategorySummaryList> {
@ -254,6 +258,7 @@ export async function fetchCategories(
}
setKibanaVersion(url);
setCapabilities(url);
return fetchUrl(url.toString()).then(JSON.parse);
}

View file

@ -70,6 +70,7 @@ export type {
InstallSource,
InstallResult,
GetCategoriesRequest,
GetPackagesRequest,
DataType,
FleetServerEnrollmentAPIKey,
FleetServerAgent,