mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[APM] Migrate `/service_maps` to deployment agnostic test (#199984)](https://github.com/elastic/kibana/pull/199984) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Sergi Romeu","email":"sergi.romeu@elastic.co"},"sourceCommit":{"committedDate":"2024-11-18T11:32:26Z","message":"[APM] Migrate `/service_maps` to deployment agnostic test (#199984)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/198983\r\nPart of https://github.com/elastic/kibana/issues/193245\r\n\r\nThis PR contains the changes to migrate `service_maps` test folder to\r\nDeployment-agnostic testing strategy.\r\n\r\n### How to test\r\n\r\n- Serverless\r\n\r\n```\r\nnode scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts\r\nnode scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep=\"APM\"\r\n```\r\n\r\nIt's recommended to be run against\r\n[MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki)\r\n\r\n- Stateful\r\n```\r\nnode scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts\r\nnode scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep=\"APM\"\r\n```\r\n\r\n## Checks\r\n\r\n- [ ] (OPTIONAL, only if a test has been unskipped) Run flaky test suite\r\n- [x] local run for serverless\r\n- [x] local run for stateful\r\n- [x] MKI run for serverless\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"53d05233a5484f7071167392e762ee4dd4dcda9c","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","apm","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-infra_services"],"title":"[APM] Migrate `/service_maps` to deployment agnostic test","number":199984,"url":"https://github.com/elastic/kibana/pull/199984","mergeCommit":{"message":"[APM] Migrate `/service_maps` to deployment agnostic test (#199984)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/198983\r\nPart of https://github.com/elastic/kibana/issues/193245\r\n\r\nThis PR contains the changes to migrate `service_maps` test folder to\r\nDeployment-agnostic testing strategy.\r\n\r\n### How to test\r\n\r\n- Serverless\r\n\r\n```\r\nnode scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts\r\nnode scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep=\"APM\"\r\n```\r\n\r\nIt's recommended to be run against\r\n[MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki)\r\n\r\n- Stateful\r\n```\r\nnode scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts\r\nnode scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep=\"APM\"\r\n```\r\n\r\n## Checks\r\n\r\n- [ ] (OPTIONAL, only if a test has been unskipped) Run flaky test suite\r\n- [x] local run for serverless\r\n- [x] local run for stateful\r\n- [x] MKI run for serverless\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"53d05233a5484f7071167392e762ee4dd4dcda9c"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/199984","number":199984,"mergeCommit":{"message":"[APM] Migrate `/service_maps` to deployment agnostic test (#199984)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/198983\r\nPart of https://github.com/elastic/kibana/issues/193245\r\n\r\nThis PR contains the changes to migrate `service_maps` test folder to\r\nDeployment-agnostic testing strategy.\r\n\r\n### How to test\r\n\r\n- Serverless\r\n\r\n```\r\nnode scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts\r\nnode scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep=\"APM\"\r\n```\r\n\r\nIt's recommended to be run against\r\n[MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki)\r\n\r\n- Stateful\r\n```\r\nnode scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts\r\nnode scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep=\"APM\"\r\n```\r\n\r\n## Checks\r\n\r\n- [ ] (OPTIONAL, only if a test has been unskipped) Run flaky test suite\r\n- [x] local run for serverless\r\n- [x] local run for stateful\r\n- [x] MKI run for serverless\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"53d05233a5484f7071167392e762ee4dd4dcda9c"}}]}] BACKPORT--> Co-authored-by: Sergi Romeu <sergi.romeu@elastic.co>
This commit is contained in:
parent
900a2def42
commit
27f20eec28
8 changed files with 309 additions and 298 deletions
|
@ -29,6 +29,7 @@ export default function apmApiIntegrationTests({
|
|||
loadTestFile(require.resolve('./observability_overview'));
|
||||
loadTestFile(require.resolve('./latency'));
|
||||
loadTestFile(require.resolve('./infrastructure'));
|
||||
loadTestFile(require.resolve('./service_maps'));
|
||||
loadTestFile(require.resolve('./inspect'));
|
||||
loadTestFile(require.resolve('./service_groups'));
|
||||
});
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) {
|
||||
describe('service_maps', () => {
|
||||
loadTestFile(require.resolve('./service_maps.spec.ts'));
|
||||
loadTestFile(require.resolve('./service_maps_kuery_filter.spec.ts'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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 type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
|
||||
import expect from 'expect';
|
||||
import { serviceMap, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { Readable } from 'node:stream';
|
||||
import type { SupertestReturnType } from '../../../../../../apm_api_integration/common/apm_api_supertest';
|
||||
import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
type DependencyResponse = SupertestReturnType<'GET /internal/apm/service-map/dependency'>;
|
||||
type ServiceNodeResponse =
|
||||
SupertestReturnType<'GET /internal/apm/service-map/service/{serviceName}'>;
|
||||
|
||||
export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const apmApiClient = getService('apmApi');
|
||||
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', () => {
|
||||
describe('without data', () => {
|
||||
it('returns an empty list', async () => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/service-map`,
|
||||
params: {
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.elements.length).toBe(0);
|
||||
});
|
||||
|
||||
describe('/internal/apm/service-map/service/{serviceName} without data', () => {
|
||||
let response: ServiceNodeResponse;
|
||||
before(async () => {
|
||||
response = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/service-map/service/{serviceName}`,
|
||||
params: {
|
||||
path: { serviceName: 'opbeans-node' },
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('retuns status code 200', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('returns an object with nulls', async () => {
|
||||
[
|
||||
response.body.currentPeriod?.failedTransactionsRate?.value,
|
||||
response.body.currentPeriod?.memoryUsage?.value,
|
||||
response.body.currentPeriod?.cpuUsage?.value,
|
||||
response.body.currentPeriod?.transactionStats?.latency?.value,
|
||||
response.body.currentPeriod?.transactionStats?.throughput?.value,
|
||||
].forEach((value) => {
|
||||
expect(value).toEqual(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/internal/apm/service-map/dependency', () => {
|
||||
let response: DependencyResponse;
|
||||
before(async () => {
|
||||
response = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/service-map/dependency`,
|
||||
params: {
|
||||
query: {
|
||||
dependencyName: 'postgres',
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('retuns status code 200', () => {
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('returns undefined values', () => {
|
||||
expect(response.body.currentPeriod).toEqual({ transactionStats: {} });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with synthtrace data', () => {
|
||||
let synthtraceEsClient: ApmSynthtraceEsClient;
|
||||
|
||||
before(async () => {
|
||||
synthtraceEsClient = await synthtrace.createApmSynthtraceEsClient();
|
||||
|
||||
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'],
|
||||
],
|
||||
];
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return synthtraceEsClient.index(Readable.from(Array.from(events)));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtraceEsClient.clean();
|
||||
});
|
||||
|
||||
it('returns service map elements', async () => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/service-map',
|
||||
params: {
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.elements.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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 '@kbn/expect';
|
||||
import { timerange, serviceMap } from '@kbn/apm-synthtrace-client';
|
||||
import {
|
||||
APIClientRequestParamsOf,
|
||||
APIReturnType,
|
||||
} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
|
||||
import { RecursivePartial } from '@kbn/apm-plugin/typings/common';
|
||||
import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
|
||||
import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const apmApiClient = getService('apmApi');
|
||||
const synthtrace = getService('synthtrace');
|
||||
|
||||
const start = new Date('2023-01-01T00:00:00.000Z').getTime();
|
||||
const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1;
|
||||
|
||||
async function callApi(
|
||||
overrides?: RecursivePartial<
|
||||
APIClientRequestParamsOf<'GET /internal/apm/service-map'>['params']
|
||||
>
|
||||
) {
|
||||
return await apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/service-map',
|
||||
params: {
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
...overrides?.query,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe('service map kuery filter', () => {
|
||||
let apmSynthtraceEsClient: ApmSynthtraceEsClient;
|
||||
|
||||
before(async () => {
|
||||
apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient();
|
||||
|
||||
const events = timerange(start, end)
|
||||
.interval('15m')
|
||||
.rate(1)
|
||||
.generator(
|
||||
serviceMap({
|
||||
services: [
|
||||
{ 'synthbeans-go': 'go' },
|
||||
{ 'synthbeans-java': 'java' },
|
||||
{ 'synthbeans-node': 'nodejs' },
|
||||
],
|
||||
definePaths([go, java, node]) {
|
||||
return [
|
||||
[go, java],
|
||||
[java, go, 'redis'],
|
||||
[node, 'redis'],
|
||||
{
|
||||
path: [node, java, go, 'elasticsearch'],
|
||||
transaction: (t) => t.defaults({ 'labels.name': 'node-java-go-es' }),
|
||||
},
|
||||
[go, node, java],
|
||||
];
|
||||
},
|
||||
})
|
||||
);
|
||||
await apmSynthtraceEsClient.index(events);
|
||||
});
|
||||
|
||||
after(() => apmSynthtraceEsClient.clean());
|
||||
|
||||
it('returns full service map when no kuery is defined', async () => {
|
||||
const { status, body } = await callApi();
|
||||
|
||||
expect(status).to.be(200);
|
||||
|
||||
const { nodes, edges } = partitionElements(body.elements);
|
||||
|
||||
expect(getIds(nodes)).to.eql([
|
||||
'>elasticsearch',
|
||||
'>redis',
|
||||
'synthbeans-go',
|
||||
'synthbeans-java',
|
||||
'synthbeans-node',
|
||||
]);
|
||||
expect(getIds(edges)).to.eql([
|
||||
'synthbeans-go~>elasticsearch',
|
||||
'synthbeans-go~>redis',
|
||||
'synthbeans-go~synthbeans-java',
|
||||
'synthbeans-go~synthbeans-node',
|
||||
'synthbeans-java~synthbeans-go',
|
||||
'synthbeans-node~>redis',
|
||||
'synthbeans-node~synthbeans-java',
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns only service nodes and connections filtered by given kuery', async () => {
|
||||
const { status, body } = await callApi({
|
||||
query: { kuery: `labels.name: "node-java-go-es"` },
|
||||
});
|
||||
|
||||
expect(status).to.be(200);
|
||||
|
||||
const { nodes, edges } = partitionElements(body.elements);
|
||||
|
||||
expect(getIds(nodes)).to.eql([
|
||||
'>elasticsearch',
|
||||
'synthbeans-go',
|
||||
'synthbeans-java',
|
||||
'synthbeans-node',
|
||||
]);
|
||||
expect(getIds(edges)).to.eql([
|
||||
'synthbeans-go~>elasticsearch',
|
||||
'synthbeans-java~synthbeans-go',
|
||||
'synthbeans-node~synthbeans-java',
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
type ConnectionElements = APIReturnType<'GET /internal/apm/service-map'>['elements'];
|
||||
|
||||
function partitionElements(elements: ConnectionElements) {
|
||||
const edges = elements.filter(({ data }) => 'source' in data && 'target' in data);
|
||||
const nodes = elements.filter((element) => !edges.includes(element));
|
||||
return { edges, nodes };
|
||||
}
|
||||
|
||||
function getIds(elements: ConnectionElements) {
|
||||
return elements.map(({ data }) => data.id).sort();
|
||||
}
|
|
@ -52,84 +52,6 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext)
|
|||
});
|
||||
});
|
||||
|
||||
registry.when('Service Map without data', { config: 'trial', archives: [] }, () => {
|
||||
describe('/internal/apm/service-map without data', () => {
|
||||
it('returns an empty list', async () => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/service-map`,
|
||||
params: {
|
||||
query: {
|
||||
start: metadata.start,
|
||||
end: metadata.end,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.elements.length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('/internal/apm/service-map/service/{serviceName} without data', () => {
|
||||
let response: ServiceNodeResponse;
|
||||
before(async () => {
|
||||
response = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/service-map/service/{serviceName}`,
|
||||
params: {
|
||||
path: { serviceName: 'opbeans-node' },
|
||||
query: {
|
||||
start: metadata.start,
|
||||
end: metadata.end,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('retuns status code 200', () => {
|
||||
expect(response.status).to.be(200);
|
||||
});
|
||||
|
||||
it('returns an object with nulls', async () => {
|
||||
[
|
||||
response.body.currentPeriod?.failedTransactionsRate?.value,
|
||||
response.body.currentPeriod?.memoryUsage?.value,
|
||||
response.body.currentPeriod?.cpuUsage?.value,
|
||||
response.body.currentPeriod?.transactionStats?.latency?.value,
|
||||
response.body.currentPeriod?.transactionStats?.throughput?.value,
|
||||
].forEach((value) => {
|
||||
expect(value).to.be.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/internal/apm/service-map/dependency', () => {
|
||||
let response: DependencyResponse;
|
||||
before(async () => {
|
||||
response = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/service-map/dependency`,
|
||||
params: {
|
||||
query: {
|
||||
dependencyName: 'postgres',
|
||||
start: metadata.start,
|
||||
end: metadata.end,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('retuns status code 200', () => {
|
||||
expect(response.status).to.be(200);
|
||||
});
|
||||
|
||||
it('returns undefined values', () => {
|
||||
expect(response.body.currentPeriod).to.eql({ transactionStats: {} });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
registry.when('Service Map with data', { config: 'trial', archives: ['apm_8.0.0'] }, () => {
|
||||
describe('/internal/apm/service-map with data', () => {
|
||||
let response: ServiceMapResponse;
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* 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 '@kbn/expect';
|
||||
import { timerange, serviceMap } from '@kbn/apm-synthtrace-client';
|
||||
import {
|
||||
APIClientRequestParamsOf,
|
||||
APIReturnType,
|
||||
} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
|
||||
import { RecursivePartial } from '@kbn/apm-plugin/typings/common';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const registry = getService('registry');
|
||||
const apmApiClient = getService('apmApiClient');
|
||||
const apmSynthtraceEsClient = getService('apmSynthtraceEsClient');
|
||||
|
||||
const start = new Date('2023-01-01T00:00:00.000Z').getTime();
|
||||
const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1;
|
||||
|
||||
async function callApi(
|
||||
overrides?: RecursivePartial<
|
||||
APIClientRequestParamsOf<'GET /internal/apm/service-map'>['params']
|
||||
>
|
||||
) {
|
||||
return await apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/service-map',
|
||||
params: {
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
...overrides?.query,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
registry.when('Service Map', { config: 'trial', archives: [] }, () => {
|
||||
describe('optional kuery param', () => {
|
||||
before(async () => {
|
||||
const events = timerange(start, end)
|
||||
.interval('15m')
|
||||
.rate(1)
|
||||
.generator(
|
||||
serviceMap({
|
||||
services: [
|
||||
{ 'synthbeans-go': 'go' },
|
||||
{ 'synthbeans-java': 'java' },
|
||||
{ 'synthbeans-node': 'nodejs' },
|
||||
],
|
||||
definePaths([go, java, node]) {
|
||||
return [
|
||||
[go, java],
|
||||
[java, go, 'redis'],
|
||||
[node, 'redis'],
|
||||
{
|
||||
path: [node, java, go, 'elasticsearch'],
|
||||
transaction: (t) => t.defaults({ 'labels.name': 'node-java-go-es' }),
|
||||
},
|
||||
[go, node, java],
|
||||
];
|
||||
},
|
||||
})
|
||||
);
|
||||
await apmSynthtraceEsClient.index(events);
|
||||
});
|
||||
|
||||
after(() => apmSynthtraceEsClient.clean());
|
||||
|
||||
it('returns full service map when no kuery is defined', async () => {
|
||||
const { status, body } = await callApi();
|
||||
|
||||
expect(status).to.be(200);
|
||||
|
||||
const { nodes, edges } = partitionElements(body.elements);
|
||||
|
||||
expect(getIds(nodes)).to.eql([
|
||||
'>elasticsearch',
|
||||
'>redis',
|
||||
'synthbeans-go',
|
||||
'synthbeans-java',
|
||||
'synthbeans-node',
|
||||
]);
|
||||
expect(getIds(edges)).to.eql([
|
||||
'synthbeans-go~>elasticsearch',
|
||||
'synthbeans-go~>redis',
|
||||
'synthbeans-go~synthbeans-java',
|
||||
'synthbeans-go~synthbeans-node',
|
||||
'synthbeans-java~synthbeans-go',
|
||||
'synthbeans-node~>redis',
|
||||
'synthbeans-node~synthbeans-java',
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns only service nodes and connections filtered by given kuery', async () => {
|
||||
const { status, body } = await callApi({
|
||||
query: { kuery: `labels.name: "node-java-go-es"` },
|
||||
});
|
||||
|
||||
expect(status).to.be(200);
|
||||
|
||||
const { nodes, edges } = partitionElements(body.elements);
|
||||
|
||||
expect(getIds(nodes)).to.eql([
|
||||
'>elasticsearch',
|
||||
'synthbeans-go',
|
||||
'synthbeans-java',
|
||||
'synthbeans-node',
|
||||
]);
|
||||
expect(getIds(edges)).to.eql([
|
||||
'synthbeans-go~>elasticsearch',
|
||||
'synthbeans-java~synthbeans-go',
|
||||
'synthbeans-node~synthbeans-java',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
type ConnectionElements = APIReturnType<'GET /internal/apm/service-map'>['elements'];
|
||||
|
||||
function partitionElements(elements: ConnectionElements) {
|
||||
const edges = elements.filter(({ data }) => 'source' in data && 'target' in data);
|
||||
const nodes = elements.filter((element) => !edges.includes(element));
|
||||
return { edges, nodes };
|
||||
}
|
||||
|
||||
function getIds(elements: ConnectionElements) {
|
||||
return elements.map(({ data }) => data.id).sort();
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* 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.createM2mApiKeyWithRoleScope('admin');
|
||||
|
||||
return synthtraceEsClient.index(Readable.from(Array.from(events)));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await svlUserManager.invalidateM2mApiKeyWithRoleScope(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);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -12,7 +12,6 @@ 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('./synthetics'));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue