[EDR Workflows] Initialize agent with latest fleet supported version (#189174)

This PR introduces a call to `/agents/available_versions` to fetch the
latest available agent version in Serverless environment. This version
is then used to create agents throughout our tests.
This commit is contained in:
Konrad Szwarc 2024-08-05 22:08:19 +02:00 committed by GitHub
parent 40d1a91bac
commit 4106cac4ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 210 additions and 113 deletions

View file

@ -36,7 +36,7 @@ export class EndpointRuleAlertGenerator extends BaseDataGenerator {
generate(overrides: DeepPartial<EndpointRuleAlert> = {}): EndpointRuleAlert {
const endpointMetadataGenerator = new EndpointMetadataGenerator();
const endpointMetadata = endpointMetadataGenerator.generate({
agent: { version: kibanaPackageJson.version },
agent: { version: overrides?.agent?.version ?? kibanaPackageJson.version },
host: { hostname: overrides?.host?.hostname },
Endpoint: { state: { isolation: overrides?.Endpoint?.state?.isolation } },
});
@ -50,7 +50,7 @@ export class EndpointRuleAlertGenerator extends BaseDataGenerator {
agent: {
id: endpointAgentId,
type: 'endpoint',
version: kibanaPackageJson.version,
version: endpointMetadata.agent.version,
},
elastic: endpointMetadata.elastic,
host: endpointMetadata.host,

View file

@ -15,6 +15,9 @@ import type {
MappingTypeMapping,
Name,
} from '@elastic/elasticsearch/lib/api/types';
import type { KbnClient } from '@kbn/test';
import { isServerlessKibanaFlavor } from '../utils/kibana_status';
import { fetchFleetLatestAvailableAgentVersion } from '../utils/fetch_fleet_version';
import { createToolingLogger, wrapErrorIfNeeded } from './utils';
import { DEFAULT_ALERTS_INDEX } from '../../constants';
import { EndpointRuleAlertGenerator } from '../data_generators/endpoint_rule_alert_generator';
@ -26,6 +29,7 @@ export interface IndexEndpointRuleAlertsOptions {
endpointIsolated?: boolean;
count?: number;
log?: ToolingLog;
kbnClient?: KbnClient;
}
export interface IndexedEndpointRuleAlerts {
@ -41,6 +45,7 @@ export interface DeletedIndexedEndpointRuleAlerts {
* Loads alerts for Endpoint directly into the internal index that the Endpoint Rule would have
* written them to for a given endpoint
* @param esClient
* @param kbnClient
* @param endpointAgentId
* @param endpointHostname
* @param endpointIsolated
@ -49,6 +54,7 @@ export interface DeletedIndexedEndpointRuleAlerts {
*/
export const indexEndpointRuleAlerts = async ({
esClient,
kbnClient,
endpointAgentId,
endpointHostname,
endpointIsolated,
@ -59,12 +65,20 @@ export const indexEndpointRuleAlerts = async ({
await ensureEndpointRuleAlertsIndexExists(esClient);
let version = kibanaPackageJson.version;
if (kbnClient) {
const isServerless = await isServerlessKibanaFlavor(kbnClient);
if (isServerless) {
version = await fetchFleetLatestAvailableAgentVersion(kbnClient);
}
}
const alertsGenerator = new EndpointRuleAlertGenerator();
const indexedAlerts: estypes.IndexResponse[] = [];
for (let n = 0; n < count; n++) {
const alert = alertsGenerator.generate({
agent: { id: endpointAgentId },
agent: { id: endpointAgentId, version },
host: { hostname: endpointHostname },
...(endpointIsolated ? { Endpoint: { state: { isolation: endpointIsolated } } } : {}),
});

View file

@ -23,6 +23,7 @@ import {
packagePolicyRouteService,
} from '@kbn/fleet-plugin/common';
import type { ToolingLog } from '@kbn/tooling-log';
import { fetchFleetLatestAvailableAgentVersion } from '../utils/fetch_fleet_version';
import { indexFleetServerAgent } from './index_fleet_agent';
import { catchAxiosErrorFormatAndThrow } from '../format_axios_error';
import { usageTracker } from './usage_tracker';
@ -47,6 +48,12 @@ export const enableFleetServerIfNecessary = usageTracker.track(
log: ToolingLog = createToolingLogger(),
version: string = kibanaPackageJson.version
) => {
let agentVersion = version;
if (isServerless) {
agentVersion = await fetchFleetLatestAvailableAgentVersion(kbnClient);
}
const agentPolicy = await getOrCreateFleetServerAgentPolicy(kbnClient, log);
if (!isServerless && !(await hasFleetServerAgent(esClient, agentPolicy.id))) {
@ -56,7 +63,7 @@ export const enableFleetServerIfNecessary = usageTracker.track(
const indexedAgent = await indexFleetServerAgent(esClient, log, {
policy_id: agentPolicy.id,
agent: { version },
agent: { version: agentVersion },
last_checkin_status: 'online',
last_checkin: lastCheckin.toISOString(),
});

View file

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
/**
* Fetches the latest version of the Elastic Agent available for download
* @param kbnClient
*/
import type { KbnClient } from '@kbn/test';
import { AGENT_API_ROUTES } from '@kbn/fleet-plugin/common';
import type { GetAvailableVersionsResponse } from '@kbn/fleet-plugin/common/types';
import { catchAxiosErrorFormatAndThrow } from '../format_axios_error';
export const fetchFleetLatestAvailableAgentVersion = async (
kbnClient: KbnClient
): Promise<string> => {
return kbnClient
.request<GetAvailableVersionsResponse>({
method: 'GET',
path: AGENT_API_ROUTES.AVAILABLE_VERSIONS_PATTERN,
headers: {
'elastic-api-version': '2023-10-31',
},
})
.then((response) => response.data.items[0])
.catch(catchAxiosErrorFormatAndThrow);
};

View file

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { KbnClient } from '@kbn/test';
import type { Client } from '@elastic/elasticsearch';
import type { StatusResponse } from '@kbn/core-status-common-internal';
import { catchAxiosErrorFormatAndThrow } from '../format_axios_error';
export const fetchKibanaStatus = async (kbnClient: KbnClient): Promise<StatusResponse> => {
return (await kbnClient.status.get().catch(catchAxiosErrorFormatAndThrow)) as StatusResponse;
};
/**
* Checks to see if Kibana/ES is running in serverless mode
* @param client
*/
export const isServerlessKibanaFlavor = async (client: KbnClient | Client): Promise<boolean> => {
if (client instanceof KbnClient) {
const kbnStatus = await fetchKibanaStatus(client);
// If we don't have status for plugins, then error
// the Status API will always return something (its an open API), but if auth was successful,
// it will also return more data.
if (!kbnStatus?.status?.plugins) {
throw new Error(
`Unable to retrieve Kibana plugins status (likely an auth issue with the username being used for kibana)`
);
}
return kbnStatus.status.plugins?.serverless?.level === 'available';
} else {
return (await client.info()).version.build_flavor === 'serverless';
}
};

View file

@ -56,8 +56,8 @@ describe(
policy = indexedPolicy.integrationPolicies[0];
return enableAllPolicyProtections(policy.id).then(() => {
// Create and enroll a new Endpoint host
return createEndpointHost(policy.policy_ids[0]).then((host) => {
// At this point 8.14.2 is GA and this functionality is not available until 8.15.0
return createEndpointHost(policy.policy_ids[0], '8.15.0').then((host) => {
createdHost = host as CreateAndEnrollEndpointHostResponse;
});
});

View file

@ -10,13 +10,14 @@ import type { Client } from '@elastic/elasticsearch';
import type { ToolingLog } from '@kbn/tooling-log';
import type { KbnClient } from '@kbn/test/src/kbn_client';
import { kibanaPackageJson } from '@kbn/repo-info';
import { isServerlessKibanaFlavor } from '../../../../common/endpoint/utils/kibana_status';
import { fetchFleetLatestAvailableAgentVersion } from '../../../../common/endpoint/utils/fetch_fleet_version';
import { isFleetServerRunning } from '../../../../scripts/endpoint/common/fleet_server/fleet_server_services';
import type { HostVm } from '../../../../scripts/endpoint/common/types';
import type { BaseVmCreateOptions } from '../../../../scripts/endpoint/common/vm_services';
import { createVm } from '../../../../scripts/endpoint/common/vm_services';
import {
fetchAgentPolicyEnrollmentKey,
fetchFleetAvailableVersions,
fetchFleetServerUrl,
getAgentDownloadUrl,
getAgentFileName,
@ -38,12 +39,12 @@ export interface CreateAndEnrollEndpointHostCIOptions
agentPolicyId: string;
/** version of the Agent to install. Defaults to stack version */
version?: string;
/** skip all checks and use provided version */
forceVersion?: boolean;
/** The name for the host. Will also be the name of the VM */
hostname?: string;
/** If `version` should be exact, or if this is `true`, then the closest version will be used. Defaults to `false` */
useClosestVersionMatch?: boolean;
/** If the environment is MKI */
isMkiEnvironment?: boolean;
}
export interface CreateAndEnrollEndpointHostCIResponse {
@ -66,14 +67,16 @@ export const createAndEnrollEndpointHostCI = async ({
hostname,
version = kibanaPackageJson.version,
useClosestVersionMatch = true,
isMkiEnvironment = false,
forceVersion = false,
}: CreateAndEnrollEndpointHostCIOptions): Promise<CreateAndEnrollEndpointHostCIResponse> => {
let agentVersion = version;
const vmName = hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`;
let agentVersion = version;
if (isMkiEnvironment) {
// MKI env provides own fleet server. We must be sure that currently deployed FS is compatible with agent version we want to deploy.
agentVersion = await fetchFleetAvailableVersions(kbnClient);
if (!forceVersion) {
const isServerless = await isServerlessKibanaFlavor(kbnClient);
if (isServerless) {
agentVersion = await fetchFleetLatestAvailableAgentVersion(kbnClient);
}
}
const fileNameNoExtension = getAgentFileName(agentVersion);

View file

@ -246,11 +246,12 @@ export const dataLoaders = (
},
indexEndpointRuleAlerts: async (options: { endpointAgentId: string; count?: number }) => {
const { esClient, log } = await stackServicesPromise;
const { esClient, log, kbnClient } = await stackServicesPromise;
return (
await indexEndpointRuleAlerts({
...options,
esClient,
kbnClient,
log,
})
).alerts;
@ -326,8 +327,6 @@ export const dataLoadersForRealEndpoints = (
config: Cypress.PluginConfigOptions
): void => {
const stackServicesPromise = setupStackServicesUsingCypressConfig(config);
const isServerless = Boolean(config.env.IS_SERVERLESS);
const isCloudServerless = Boolean(config.env.CLOUD_SERVERLESS);
on('task', {
createSentinelOneHost: async () => {
@ -415,7 +414,6 @@ ${s1Info.status}
options: Omit<CreateAndEnrollEndpointHostCIOptions, 'log' | 'kbnClient'>
): Promise<CreateAndEnrollEndpointHostCIResponse> => {
const { kbnClient, log, esClient } = await stackServicesPromise;
const isMkiEnvironment = isServerless && isCloudServerless;
let retryAttempt = 0;
const attemptCreateEndpointHost =
async (): Promise<CreateAndEnrollEndpointHostCIResponse> => {
@ -424,7 +422,6 @@ ${s1Info.status}
const newHost = process.env.CI
? await createAndEnrollEndpointHostCI({
useClosestVersionMatch: true,
isMkiEnvironment,
...options,
log,
kbnClient,

View file

@ -10,6 +10,7 @@ import type { KbnClient } from '@kbn/test';
import pRetry from 'p-retry';
import { kibanaPackageJson } from '@kbn/repo-info';
import type { ToolingLog } from '@kbn/tooling-log';
import { fetchFleetLatestAvailableAgentVersion } from '../../../../../common/endpoint/utils/fetch_fleet_version';
import { dump } from '../../../../../scripts/endpoint/common/utils';
import { STARTED_TRANSFORM_STATES } from '../../../../../common/constants';
import {
@ -77,8 +78,18 @@ export const cyLoadEndpointDataHandler = async (
isServerless = false,
} = options;
let agentVersion = version;
if (isServerless) {
agentVersion = await fetchFleetLatestAvailableAgentVersion(kbnClient);
}
const DocGenerator = EndpointDocGenerator.custom({
CustomMetadataGenerator: EndpointMetadataGenerator.custom({ version, os, isolation }),
CustomMetadataGenerator: EndpointMetadataGenerator.custom({
version: agentVersion,
os,
isolation,
}),
});
if (waitUntilTransformed) {
@ -192,6 +203,7 @@ const startTransform = async (
* the united metadata index
*
* @param esClient
* @param log
* @param location
* @param ids
*/

View file

@ -10,12 +10,14 @@ import type { CreateAndEnrollEndpointHostResponse } from '../../../../scripts/en
// only used in "real" endpoint tests not in mocked ones
export const createEndpointHost = (
agentPolicyId: string,
version?: string,
timeout?: number
): Cypress.Chainable<CreateAndEnrollEndpointHostResponse> => {
return cy.task(
'createEndpointHost',
{
agentPolicyId,
...(version ? { version, forceVersion: true } : {}),
},
{ timeout: timeout ?? 30 * 60 * 1000 }
);

View file

@ -11,6 +11,8 @@ import pMap from 'p-map';
import type { CreatePackagePolicyResponse } from '@kbn/fleet-plugin/common';
import type { ToolingLog } from '@kbn/tooling-log';
import { kibanaPackageJson } from '@kbn/repo-info';
import { isServerlessKibanaFlavor } from '../../../../common/endpoint/utils/kibana_status';
import { fetchFleetLatestAvailableAgentVersion } from '../../../../common/endpoint/utils/fetch_fleet_version';
import { indexAlerts } from '../../../../common/endpoint/data_loaders/index_alerts';
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
import { fetchEndpointMetadataList } from '../../common/endpoint_metadata_services';
@ -21,16 +23,9 @@ import { METADATA_DATASTREAM } from '../../../../common/endpoint/constants';
import { EndpointMetadataGenerator } from '../../../../common/endpoint/data_generators/endpoint_metadata_generator';
import { getEndpointPackageInfo } from '../../../../common/endpoint/utils/package';
import { ENDPOINT_ALERTS_INDEX, ENDPOINT_EVENTS_INDEX } from '../../common/constants';
import { isServerlessKibanaFlavor } from '../../common/stack_services';
let WAS_FLEET_SETUP_DONE = false;
const CurrentKibanaVersionDocGenerator = EndpointDocGenerator.custom({
CustomMetadataGenerator: EndpointMetadataGenerator.custom({
version: kibanaPackageJson.version,
}),
});
export const loadEndpointsIfNoneExist = async (
esClient: Client,
kbnClient: KbnClient,
@ -84,14 +79,20 @@ export const loadEndpoints = async ({
log,
onProgress,
count = 2,
DocGeneratorClass = CurrentKibanaVersionDocGenerator,
DocGeneratorClass,
}: LoadEndpointsOptions): Promise<void> => {
if (log) {
log.verbose(`loadEndpoints(): Loading ${count} endpoints...`);
}
const isServerless = await isServerlessKibanaFlavor(kbnClient);
let version = kibanaPackageJson.version;
if (isServerless) {
version = await fetchFleetLatestAvailableAgentVersion(kbnClient);
}
if (!WAS_FLEET_SETUP_DONE) {
const isServerless = await isServerlessKibanaFlavor(kbnClient);
await setupFleetForEndpoint(kbnClient);
await enableFleetServerIfNecessary(esClient, isServerless, kbnClient, log);
// eslint-disable-next-line require-atomic-updates
@ -120,10 +121,16 @@ export const loadEndpoints = async ({
}
};
const CurrentKibanaVersionDocGenerator = EndpointDocGenerator.custom({
CustomMetadataGenerator: EndpointMetadataGenerator.custom({
version,
}),
});
await pMap(
Array.from({ length: count }),
async () => {
const endpointGenerator = new DocGeneratorClass();
const endpointGenerator = new (DocGeneratorClass ?? CurrentKibanaVersionDocGenerator)();
await indexEndpointHostDocs({
numDocs: 1,

View file

@ -8,7 +8,8 @@
import type { Client, estypes } from '@elastic/elasticsearch';
import assert from 'assert';
import type { ToolingLog } from '@kbn/tooling-log';
import { createEsClient, isServerlessKibanaFlavor } from './stack_services';
import { isServerlessKibanaFlavor } from '../../../common/endpoint/utils/kibana_status';
import { createEsClient } from './stack_services';
import type { CreatedSecuritySuperuser } from './security_user_services';
import { createSecuritySuperuser } from './security_user_services';

View file

@ -8,6 +8,8 @@
import { kibanaPackageJson } from '@kbn/repo-info';
import type { KbnClient } from '@kbn/test';
import type { ToolingLog } from '@kbn/tooling-log';
import { isServerlessKibanaFlavor } from '../../../common/endpoint/utils/kibana_status';
import { fetchFleetLatestAvailableAgentVersion } from '../../../common/endpoint/utils/fetch_fleet_version';
import { prefixedOutputLogger } from './utils';
import type { HostVm } from './types';
import type { BaseVmCreateOptions } from './vm_services';
@ -23,6 +25,8 @@ export interface CreateAndEnrollEndpointHostOptions
agentPolicyId: string;
/** version of the Agent to install. Defaults to stack version */
version?: string;
/** skip all checks and use provided version */
forceVersion?: boolean;
/** The name for the host. Will also be the name of the VM */
hostname?: string;
/** If `version` should be exact, or if this is `true`, then the closest version will be used. Defaults to `false` */
@ -49,13 +53,22 @@ export const createAndEnrollEndpointHost = async ({
memory,
hostname,
version = kibanaPackageJson.version,
forceVersion = false,
useClosestVersionMatch = false,
useCache = true,
}: CreateAndEnrollEndpointHostOptions): Promise<CreateAndEnrollEndpointHostResponse> => {
const log = prefixedOutputLogger('createAndEnrollEndpointHost()', _log);
let agentVersion = version;
if (!forceVersion) {
const isServerless = await isServerlessKibanaFlavor(kbnClient);
if (isServerless) {
agentVersion = await fetchFleetLatestAvailableAgentVersion(kbnClient);
}
}
const isRunningInCI = Boolean(process.env.CI);
const vmName = hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`;
const { url: agentUrl } = await getAgentDownloadUrl(version, useClosestVersionMatch, log);
const { url: agentUrl } = await getAgentDownloadUrl(agentVersion, useClosestVersionMatch, log);
const agentDownload = isRunningInCI ? await downloadAndStoreAgent(agentUrl) : undefined;
// TODO: remove dependency on env. var and keep function pure
@ -84,7 +97,7 @@ export const createAndEnrollEndpointHost = async ({
log,
hostVm,
agentPolicyId,
version,
version: agentVersion,
closestVersionMatch: useClosestVersionMatch,
useAgentCache: useCache,
});

View file

@ -41,9 +41,9 @@ import {
} from '@kbn/dev-utils';
import { maybeCreateDockerNetwork, SERVERLESS_NODES, verifyDockerInstalled } from '@kbn/es';
import { resolve } from 'path';
import { isServerlessKibanaFlavor } from '../../../../common/endpoint/utils/kibana_status';
import { captureCallingStack, dump, prefixedOutputLogger } from '../utils';
import { createToolingLogger } from '../../../../common/endpoint/data_loaders/utils';
import { isServerlessKibanaFlavor } from '../stack_services';
import type { FormattedAxiosError } from '../../../../common/endpoint/format_axios_error';
import { catchAxiosErrorFormatAndThrow } from '../../../../common/endpoint/format_axios_error';
import {

View file

@ -11,6 +11,7 @@ import type {
Agent,
AgentPolicy,
AgentStatus,
CopyAgentPolicyResponse,
CreateAgentPolicyRequest,
CreateAgentPolicyResponse,
CreatePackagePolicyRequest,
@ -24,7 +25,6 @@ import type {
GetPackagePoliciesResponse,
PackagePolicy,
PostFleetSetupResponse,
CopyAgentPolicyResponse,
} from '@kbn/fleet-plugin/common';
import {
AGENT_API_ROUTES,
@ -37,8 +37,8 @@ import {
APP_API_ROUTES,
epmRouteService,
PACKAGE_POLICY_API_ROUTES,
SETUP_API_ROUTE,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
SETUP_API_ROUTE,
} from '@kbn/fleet-plugin/common';
import type { ToolingLog } from '@kbn/tooling-log';
import type { KbnClient } from '@kbn/test';
@ -49,6 +49,7 @@ import {
outputRoutesService,
} from '@kbn/fleet-plugin/common/services';
import type {
CopyAgentPolicyRequest,
DeleteAgentPolicyResponse,
EnrollmentAPIKey,
GenerateServiceTokenResponse,
@ -56,12 +57,12 @@ import type {
GetEnrollmentAPIKeysResponse,
GetOutputsResponse,
PostAgentUnenrollResponse,
CopyAgentPolicyRequest,
} from '@kbn/fleet-plugin/common/types';
import semver from 'semver';
import axios from 'axios';
import { userInfo } from 'os';
import pRetry from 'p-retry';
import { fetchKibanaStatus } from '../../../common/endpoint/utils/kibana_status';
import { isFleetServerRunning } from './fleet_server/fleet_server_services';
import { getEndpointPackageInfo } from '../../../common/endpoint/utils/package';
import type { DownloadAndStoreAgentResponse } from './agent_downloads_service';
@ -72,7 +73,6 @@ import {
RETRYABLE_TRANSIENT_ERRORS,
retryOnError,
} from '../../../common/endpoint/data_loaders/utils';
import { fetchKibanaStatus } from './stack_services';
import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fleet_agent_generator';
@ -524,24 +524,6 @@ export const getAgentDownloadUrl = async (
};
};
/**
* Fetches the latest version of the Elastic Agent available for download
* @param kbnClient
*/
export const fetchFleetAvailableVersions = async (kbnClient: KbnClient): Promise<string> => {
return kbnClient
.request<{ items: string[] }>({
method: 'GET',
path: AGENT_API_ROUTES.AVAILABLE_VERSIONS_PATTERN,
headers: {
'elastic-api-version': '2023-10-31',
},
})
.then((response) => response.data.items[0])
.catch(catchAxiosErrorFormatAndThrow);
};
/**
* Given a stack version number, function will return the closest Agent download version available
* for download. THis could be the actual version passed in or lower.

View file

@ -9,7 +9,6 @@ import { Client } from '@elastic/elasticsearch';
import type { ToolingLog } from '@kbn/tooling-log';
import type { KbnClientOptions } from '@kbn/test';
import { KbnClient } from '@kbn/test';
import type { StatusResponse } from '@kbn/core-status-common-internal';
import pRetry from 'p-retry';
import type { ReqOptions } from '@kbn/test/src/kbn_client/kbn_client_requester';
import { type AxiosResponse } from 'axios';
@ -17,8 +16,11 @@ import type { ClientOptions } from '@elastic/elasticsearch/lib/client';
import fs from 'fs';
import { CA_CERT_PATH } from '@kbn/dev-utils';
import { omit } from 'lodash';
import {
fetchKibanaStatus,
isServerlessKibanaFlavor,
} from '../../../common/endpoint/utils/kibana_status';
import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils';
import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import { isLocalhost } from './is_localhost';
import { getLocalhostRealIp } from './network_services';
import { createSecuritySuperuser } from './security_user_services';
@ -313,10 +315,6 @@ export const fetchStackVersion = async (kbnClient: KbnClient): Promise<string> =
return status.version.number;
};
export const fetchKibanaStatus = async (kbnClient: KbnClient): Promise<StatusResponse> => {
return (await kbnClient.status.get().catch(catchAxiosErrorFormatAndThrow)) as StatusResponse;
};
/**
* Checks to ensure Kibana is up and running
* @param kbnClient
@ -335,26 +333,3 @@ export const waitForKibana = async (kbnClient: KbnClient): Promise<void> => {
{ maxTimeout: 10000 }
);
};
/**
* Checks to see if Kibana/ES is running in serverless mode
* @param client
*/
export const isServerlessKibanaFlavor = async (client: KbnClient | Client): Promise<boolean> => {
if (client instanceof KbnClient) {
const kbnStatus = await fetchKibanaStatus(client);
// If we don't have status for plugins, then error
// the Status API will always return something (its an open API), but if auth was successful,
// it will also return more data.
if (!kbnStatus?.status?.plugins) {
throw new Error(
`Unable to retrieve Kibana plugins status (likely an auth issue with the username being used for kibana)`
);
}
return kbnStatus.status.plugins?.serverless?.level === 'available';
} else {
return (await client.info()).version.build_flavor === 'serverless';
}
};

View file

@ -14,13 +14,14 @@ import { CA_CERT_PATH } from '@kbn/dev-utils';
import type { ToolingLog } from '@kbn/tooling-log';
import type { KbnClientOptions } from '@kbn/test';
import { KbnClient } from '@kbn/test';
import { isServerlessKibanaFlavor } from '../../common/endpoint/utils/kibana_status';
import { createToolingLogger } from '../../common/endpoint/data_loaders/utils';
import { EndpointSecurityTestRolesLoader } from './common/role_and_user_loader';
import { METADATA_DATASTREAM } from '../../common/endpoint/constants';
import { EndpointMetadataGenerator } from '../../common/endpoint/data_generators/endpoint_metadata_generator';
import { indexHostsAndAlerts } from '../../common/endpoint/index_data';
import { ANCESTRY_LIMIT, EndpointDocGenerator } from '../../common/endpoint/generate_data';
import { fetchStackVersion, isServerlessKibanaFlavor } from './common/stack_services';
import { fetchStackVersion } from './common/stack_services';
import { ENDPOINT_ALERTS_INDEX, ENDPOINT_EVENTS_INDEX } from './common/constants';
main();

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows';
import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows';
import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows';
import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows';
import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows';
import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows';
import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows';
import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users';

View file

@ -6,7 +6,7 @@
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../configs/ftr_provider_context';
export default function (providerContext: FtrProviderContext) {

View file

@ -6,7 +6,7 @@
*/
import { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrProviderContext } from '../../configs/ftr_provider_context';
export default function (providerContext: FtrProviderContext) {

View file

@ -7,18 +7,17 @@
/* eslint-disable max-classes-per-file */
import { errors } from '@elastic/elasticsearch';
import { Client } from '@elastic/elasticsearch';
import { Client, errors } from '@elastic/elasticsearch';
import { AGENTS_INDEX } from '@kbn/fleet-plugin/common';
import {
metadataCurrentIndexPattern,
metadataTransformPrefix,
HOST_METADATA_GET_ROUTE,
METADATA_CURRENT_TRANSFORM_V2,
METADATA_DATASTREAM,
METADATA_UNITED_INDEX,
METADATA_UNITED_TRANSFORM,
METADATA_UNITED_TRANSFORM_V2,
HOST_METADATA_GET_ROUTE,
METADATA_DATASTREAM,
metadataCurrentIndexPattern,
metadataTransformPrefix,
} from '@kbn/security-solution-plugin/common/endpoint/constants';
import {
deleteIndexedHostsAndAlerts,
@ -38,20 +37,33 @@ import { merge } from 'lodash';
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
import { kibanaPackageJson } from '@kbn/repo-info';
import seedrandom from 'seedrandom';
import { fetchFleetLatestAvailableAgentVersion } from '@kbn/security-solution-plugin/common/endpoint/utils/fetch_fleet_version';
import { KbnClient } from '@kbn/test';
import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/common/endpoint/utils/kibana_status';
import { FtrService } from '../../functional/ftr_provider_context';
// Document Generator override that uses a custom Endpoint Metadata generator and sets the
// `agent.version` to the current version
const CurrentKibanaVersionDocGenerator = class extends EndpointDocGenerator {
constructor(seedValue: string | seedrandom.prng) {
const MetadataGenerator = class extends EndpointMetadataGenerator {
protected randomVersion(): string {
return kibanaPackageJson.version;
}
};
super(seedValue, MetadataGenerator);
const createDocGeneratorClass = async (kbnClient: KbnClient, isServerless: boolean) => {
let version = kibanaPackageJson.version;
if (isServerless) {
version = await fetchFleetLatestAvailableAgentVersion(kbnClient);
}
// TS doesn't like the `version` let being used in the class definition
const capturedVersion = version;
return class extends EndpointDocGenerator {
constructor(seedValue: string | seedrandom.prng) {
const MetadataGenerator = class extends EndpointMetadataGenerator {
protected randomVersion(): string {
return capturedVersion;
}
};
super(seedValue, MetadataGenerator);
}
};
};
export class EndpointTestResources extends FtrService {
@ -93,7 +105,7 @@ export class EndpointTestResources extends FtrService {
* @param [options.numHostDocs=1] Number of Document to be loaded per Endpoint Host (Endpoint hosts index uses a append-only index)
* @param [options.alertsPerHost=1] Number of Alerts and Events to be loaded per Endpoint Host
* @param [options.enableFleetIntegration=true] When set to `true`, Fleet data will also be loaded (ex. Integration Policies, Agent Policies, "fake" Agents)
* @param [options.generatorSeed='seed`] The seed to be used by the data generator. Important in order to ensure the same data is generated on very run.
* @param [options.generatorSeed='seed'] The seed to be used by the data generator. Important in order to ensure the same data is generated on very run.
* @param [options.waitUntilTransformed=true] If set to `true`, the data loading process will wait until the endpoint hosts metadata is processed by the transform
* @param [options.waitTimeout=120000] If waitUntilTransformed=true, number of ms to wait until timeout
* @param [options.customIndexFn] If provided, will use this function to generate and index data instead
@ -140,6 +152,12 @@ export class EndpointTestResources extends FtrService {
await this.stopTransform(unitedTransformName);
}
const isServerless = await isServerlessKibanaFlavor(this.kbnClient);
const CurrentKibanaVersionDocGenerator = await createDocGeneratorClass(
this.kbnClient,
isServerless
);
// load data into the system
const indexedData = customIndexFn
? await customIndexFn()
@ -308,15 +326,13 @@ export class EndpointTestResources extends FtrService {
* @param endpointAgentId
*/
async fetchEndpointMetadata(endpointAgentId: string): Promise<HostInfo> {
const metadata = this.supertest
return this.supertest
.get(HOST_METADATA_GET_ROUTE.replace('{id}', endpointAgentId))
.set('kbn-xsrf', 'true')
.set('Elastic-Api-Version', '2023-10-31')
.send()
.expect(200)
.then((response) => response.body as HostInfo);
return metadata;
}
/**