[APM] Add tests for service_map and critical_path endpoints to serverless integration tests suite (#186466)

closes [186450](https://github.com/elastic/kibana/issues/186450)

## Summary

Add tests for aggregate critical path and service map endpoints to
serverless test suite

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Carlos Crespo 2024-06-21 01:00:53 +02:00 committed by GitHub
parent 8f01b66ef2
commit 4db61fa913
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 248 additions and 0 deletions

View file

@ -14,6 +14,7 @@ import { SamlToolsProvider } from './saml_tools';
import { SvlCasesServiceProvider } from './svl_cases';
import { SloApiProvider } from './slo_api';
import { TransformProvider } from './transform';
import { SynthtraceProvider } from './synthtrace';
export const services = {
// deployment agnostic FTR services
@ -26,6 +27,7 @@ export const services = {
svlCases: SvlCasesServiceProvider,
sloApi: SloApiProvider,
transform: TransformProvider,
synthtrace: SynthtraceProvider,
};
export type InheritedFtrProviderContext = GenericFtrProviderContext<typeof services, {}>;

View file

@ -0,0 +1,61 @@
/*
* 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 { Client } from '@elastic/elasticsearch';
import {
ApmSynthtraceEsClient,
ApmSynthtraceKibanaClient,
createLogger,
LogLevel,
} from '@kbn/apm-synthtrace';
import url, { format, UrlObject } from 'url';
import { FtrProviderContext } from '../ftr_provider_context';
async function getSynthtraceEsClient(client: Client, kibanaClient: ApmSynthtraceKibanaClient) {
const kibanaVersion = await kibanaClient.fetchLatestApmPackageVersion();
await kibanaClient.installApmPackage(kibanaVersion);
const esClient = new ApmSynthtraceEsClient({
client,
logger: createLogger(LogLevel.info),
version: kibanaVersion,
refreshAfterIndex: true,
});
return esClient;
}
function getSynthtraceKibanaClient(kibanaServerUrl: string) {
const kibanaServerUrlWithAuth = url
.format({
...url.parse(kibanaServerUrl),
})
.slice(0, -1);
const kibanaClient = new ApmSynthtraceKibanaClient({
target: kibanaServerUrlWithAuth,
logger: createLogger(LogLevel.debug),
});
return kibanaClient;
}
export function SynthtraceProvider({ getService }: FtrProviderContext) {
const es = getService('es');
const config = getService('config');
const servers = config.get('servers');
const kibanaServer = servers.kibana as UrlObject;
const kibanaServerUrl = format(kibanaServer);
const synthtraceKibanaClient = getSynthtraceKibanaClient(kibanaServerUrl);
return {
createSynthtraceKibanaClient: getSynthtraceKibanaClient,
async createSynthtraceEsClient() {
return getSynthtraceEsClient(es, synthtraceKibanaClient);
},
};
}

View file

@ -0,0 +1,84 @@
/*
* 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 { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
import expect from 'expect';
import { serviceMap, timerange } from '@kbn/apm-synthtrace-client';
import { Readable } from 'stream';
import type { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services';
import { APMFtrContextProvider } from '../common/services';
export default function ({ getService }: APMFtrContextProvider) {
const apmApiClient = getService('apmApiClient');
const svlUserManager = getService('svlUserManager');
const svlCommonApi = getService('svlCommonApi');
const synthtrace = getService('synthtrace');
const start = new Date('2024-06-01T00:00:00.000Z').getTime();
const end = new Date('2024-06-01T00:01:00.000Z').getTime();
describe('APM Service maps', () => {
let roleAuthc: RoleCredentials;
let internalReqHeader: InternalRequestHeader;
let synthtraceEsClient: ApmSynthtraceEsClient;
before(async () => {
synthtraceEsClient = await synthtrace.createSynthtraceEsClient();
const events = timerange(start, end)
.interval('10s')
.rate(3)
.generator(
serviceMap({
services: [
{ 'frontend-rum': 'rum-js' },
{ 'frontend-node': 'nodejs' },
{ advertService: 'java' },
],
definePaths([rum, node, adv]) {
return [
[
[rum, 'fetchAd'],
[node, 'GET /nodejs/adTag'],
[adv, 'APIRestController#getAd'],
['elasticsearch', 'GET ad-*/_search'],
],
];
},
})
);
internalReqHeader = svlCommonApi.getInternalRequestHeader();
roleAuthc = await svlUserManager.createApiKeyForRole('admin');
return synthtraceEsClient.index(Readable.from(Array.from(events)));
});
after(async () => {
await svlUserManager.invalidateApiKeyForRole(roleAuthc);
return synthtraceEsClient.clean();
});
it('returns service map elements', async () => {
const response = await apmApiClient.slsUser({
endpoint: 'GET /internal/apm/service-map',
params: {
query: {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment: 'ENVIRONMENT_ALL',
},
},
roleAuthc,
internalReqHeader,
});
expect(response.status).toBe(200);
expect(response.body.elements.length).toBeGreaterThan(0);
});
});
}

View file

@ -0,0 +1,99 @@
/*
* 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 expect from 'expect';
import { apm, ApmFields, SynthtraceGenerator, timerange } from '@kbn/apm-synthtrace-client';
import { compact, uniq } from 'lodash';
import { Readable } from 'stream';
import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
import type { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services';
import { APMFtrContextProvider } from '../common/services';
export default function ({ getService }: APMFtrContextProvider) {
const apmApiClient = getService('apmApiClient');
const svlUserManager = getService('svlUserManager');
const svlCommonApi = getService('svlCommonApi');
const synthtrace = getService('synthtrace');
const start = new Date('2022-01-01T00:00:00.000Z').getTime();
const end = new Date('2022-01-01T00:15:00.000Z').getTime() - 1;
async function fetchAndBuildCriticalPathTree(
synthtraceEsClient: ApmSynthtraceEsClient,
options: {
fn: () => SynthtraceGenerator<ApmFields>;
roleAuthc: RoleCredentials;
internalReqHeader: InternalRequestHeader;
} & ({ serviceName: string; transactionName: string } | {})
) {
const { fn, roleAuthc, internalReqHeader } = options;
const generator = fn();
const unserialized = Array.from(generator);
const serialized = unserialized.flatMap((event) => event.serialize());
const traceIds = compact(uniq(serialized.map((event) => event['trace.id'])));
await synthtraceEsClient.index(Readable.from(unserialized));
return apmApiClient.slsUser({
endpoint: 'POST /internal/apm/traces/aggregated_critical_path',
params: {
body: {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
traceIds,
serviceName: 'serviceName' in options ? options.serviceName : null,
transactionName: 'transactionName' in options ? options.transactionName : null,
},
},
roleAuthc,
internalReqHeader,
});
}
describe('APM Aggregated critical path', () => {
let roleAuthc: RoleCredentials;
let internalReqHeader: InternalRequestHeader;
let synthtraceEsClient: ApmSynthtraceEsClient;
before(async () => {
synthtraceEsClient = await synthtrace.createSynthtraceEsClient();
internalReqHeader = svlCommonApi.getInternalRequestHeader();
roleAuthc = await svlUserManager.createApiKeyForRole('admin');
});
after(async () => {
await svlUserManager.invalidateApiKeyForRole(roleAuthc);
return synthtraceEsClient.clean();
});
it('returns service map elements', async () => {
const java = apm
.service({ name: 'java', environment: 'production', agentName: 'java' })
.instance('java');
const duration = 1000;
const rate = 10;
const response = await fetchAndBuildCriticalPathTree(synthtraceEsClient, {
fn: () =>
timerange(start, end)
.interval('15m')
.rate(rate)
.generator((timestamp) => {
return java.transaction('GET /api').timestamp(timestamp).duration(duration);
}),
roleAuthc,
internalReqHeader,
});
expect(response.status).toBe(200);
expect(response.body.criticalPath).not.toBeUndefined();
});
});
}

View file

@ -12,6 +12,8 @@ export default function ({ loadTestFile }: FtrProviderContext) {
this.tags(['esGate']);
loadTestFile(require.resolve('./apm_api_integration/feature_flags.ts'));
loadTestFile(require.resolve('./apm_api_integration/service_maps/service_maps'));
loadTestFile(require.resolve('./apm_api_integration/traces/critical_path'));
loadTestFile(require.resolve('./cases'));
loadTestFile(require.resolve('./burn_rate_rule/burn_rate_rule'));
loadTestFile(require.resolve('./es_query_rule/es_query_rule'));