Skip to content

Commit

Permalink
[8.x] [APM] Migrate time_range_metadata test to deployment agnostic (e…
Browse files Browse the repository at this point in the history
…lastic#200146) (elastic#200624)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[APM] Migrate time_range_metadata test to deployment agnostic
(elastic#200146)](elastic#200146)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Carlos
Crespo","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-11-18T17:28:49Z","message":"[APM]
Migrate time_range_metadata test to deployment agnostic (elastic#200146)\n\n##
Summary\r\n\r\nCloses
[elastic#198994](https://github.com/elastic/kibana/issues/198994)\r\nPart of
https://github.com/elastic/kibana/issues/193245\r\n\r\nThis PR contains
the changes to migrate `time_range_metadata` test\r\nfolder to
Deployment-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- [ ] ~(OPTIONAL, only if a test has been
unskipped) Run flaky test\r\nsuite~\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: Elastic Machine
<[email protected]>\r\nCo-authored-by:
kibanamachine
<[email protected]>","sha":"9d314b9fbb6dc850da7341949765243cd0bf742c","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-infra_services"],"title":"[APM]
Migrate time_range_metadata test to deployment
agnostic","number":200146,"url":"https://github.com/elastic/kibana/pull/200146","mergeCommit":{"message":"[APM]
Migrate time_range_metadata test to deployment agnostic (elastic#200146)\n\n##
Summary\r\n\r\nCloses
[elastic#198994](https://github.com/elastic/kibana/issues/198994)\r\nPart of
https://github.com/elastic/kibana/issues/193245\r\n\r\nThis PR contains
the changes to migrate `time_range_metadata` test\r\nfolder to
Deployment-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- [ ] ~(OPTIONAL, only if a test has been
unskipped) Run flaky test\r\nsuite~\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: Elastic Machine
<[email protected]>\r\nCo-authored-by:
kibanamachine
<[email protected]>","sha":"9d314b9fbb6dc850da7341949765243cd0bf742c"}},"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/200146","number":200146,"mergeCommit":{"message":"[APM]
Migrate time_range_metadata test to deployment agnostic (elastic#200146)\n\n##
Summary\r\n\r\nCloses
[elastic#198994](https://github.com/elastic/kibana/issues/198994)\r\nPart of
https://github.com/elastic/kibana/issues/193245\r\n\r\nThis PR contains
the changes to migrate `time_range_metadata` test\r\nfolder to
Deployment-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- [ ] ~(OPTIONAL, only if a test has been
unskipped) Run flaky test\r\nsuite~\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: Elastic Machine
<[email protected]>\r\nCo-authored-by:
kibanamachine
<[email protected]>","sha":"9d314b9fbb6dc850da7341949765243cd0bf742c"}}]}]
BACKPORT-->

Co-authored-by: Carlos Crespo <[email protected]>
  • Loading branch information
kibanamachine and crespocarlos authored Nov 18, 2024
1 parent 779c9ba commit 8a49c1f
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 314 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default function apmApiIntegrationTests({
loadTestFile(require.resolve('./service_maps'));
loadTestFile(require.resolve('./inspect'));
loadTestFile(require.resolve('./service_groups'));
loadTestFile(require.resolve('./time_range_metadata'));
loadTestFile(require.resolve('./diagnostics'));
loadTestFile(require.resolve('./service_nodes'));
loadTestFile(require.resolve('./span_links'));
Expand Down
Original file line number Diff line number Diff line change
@@ -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('time_range_metadata', () => {
loadTestFile(require.resolve('./many_apm_server_versions.spec.ts'));
loadTestFile(require.resolve('./time_range_metadata.spec.ts'));
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
/*
* 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 { apm, timerange } from '@kbn/apm-synthtrace-client';
import moment from 'moment';
import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
import {
TRANSACTION_DURATION_HISTOGRAM,
TRANSACTION_DURATION_SUMMARY,
} from '@kbn/apm-plugin/common/es_fields/apm';
import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type';
import { RollupInterval } from '@kbn/apm-plugin/common/rollup';
import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types';
import { Readable } from 'stream';
import type { ApmApiClient } from '../../../../services/apm_api';
import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context';

export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) {
const apmApiClient = getService('apmApi');
const synthtrace = getService('synthtrace');
const es = getService('es');

const baseTime = new Date('2023-10-01T00:00:00.000Z').getTime();
const startLegacy = moment(baseTime).add(0, 'minutes');
const start = moment(baseTime).add(5, 'minutes');
const endLegacy = moment(baseTime).add(10, 'minutes');
const end = moment(baseTime).add(15, 'minutes');

describe('Time range metadata when there are multiple APM Server versions', () => {
describe('when ingesting traces from APM Server with different versions', () => {
let apmSynthtraceEsClient: ApmSynthtraceEsClient;

before(async () => {
apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient();
await generateTraceDataForService({
serviceName: 'synth-java-legacy',
start: startLegacy,
end: endLegacy,
isLegacy: true,
synthtrace: apmSynthtraceEsClient,
});

await generateTraceDataForService({
serviceName: 'synth-java',
start,
end,
isLegacy: false,
synthtrace: apmSynthtraceEsClient,
});
});

after(() => {
return apmSynthtraceEsClient.clean();
});

it('ingests transaction metrics with transaction.duration.summary', async () => {
const res = await es.search({
index: 'metrics-apm*',
body: {
query: {
bool: {
filter: [
{ exists: { field: TRANSACTION_DURATION_HISTOGRAM } },
{ exists: { field: TRANSACTION_DURATION_SUMMARY } },
],
},
},
},
});

// @ts-expect-error
expect(res.hits.total.value).to.be(20);
});

it('ingests transaction metrics without transaction.duration.summary', async () => {
const res = await es.search({
index: 'metrics-apm*',
body: {
query: {
bool: {
filter: [{ exists: { field: TRANSACTION_DURATION_HISTOGRAM } }],
must_not: [{ exists: { field: TRANSACTION_DURATION_SUMMARY } }],
},
},
},
});

// @ts-expect-error
expect(res.hits.total.value).to.be(10);
});

it('has transaction.duration.summary field for every document type', async () => {
const response = await apmApiClient.readUser({
endpoint: 'GET /internal/apm/time_range_metadata',
params: {
query: {
start: endLegacy.toISOString(),
end: end.toISOString(),
enableContinuousRollups: true,
enableServiceTransactionMetrics: true,
useSpanName: false,
kuery: '',
},
},
});

const allHasSummaryField = response.body.sources
.filter(
(source) =>
source.documentType !== ApmDocumentType.TransactionEvent &&
source.rollupInterval !== RollupInterval.SixtyMinutes // there is not enough data for 60 minutes
)
.every((source) => {
return source.hasDurationSummaryField;
});

expect(allHasSummaryField).to.eql(true);
});

it('does not support transaction.duration.summary when the field is not supported by all APM server versions', async () => {
const response = await apmApiClient.readUser({
endpoint: 'GET /internal/apm/time_range_metadata',
params: {
query: {
start: startLegacy.toISOString(),
end: endLegacy.toISOString(),
enableContinuousRollups: true,
enableServiceTransactionMetrics: true,
useSpanName: false,
kuery: '',
},
},
});

const allHasSummaryField = response.body.sources.every((source) => {
return source.hasDurationSummaryField;
});

expect(allHasSummaryField).to.eql(false);
});

it('does not support transaction.duration.summary for transactionMetric 1m when not all documents within the range support it ', async () => {
const response = await apmApiClient.readUser({
endpoint: 'GET /internal/apm/time_range_metadata',
params: {
query: {
start: startLegacy.toISOString(),
end: end.toISOString(),
enableContinuousRollups: true,
enableServiceTransactionMetrics: true,
useSpanName: false,
kuery: '',
},
},
});

const hasDurationSummaryField = response.body.sources.find(
(source) =>
source.documentType === ApmDocumentType.TransactionMetric &&
source.rollupInterval === RollupInterval.OneMinute // there is not enough data for 60 minutes in the timerange defined for the tests
)?.hasDurationSummaryField;

expect(hasDurationSummaryField).to.eql(false);
});

it('does not have latency data for synth-java-legacy', async () => {
const res = await getLatencyChartForService({
serviceName: 'synth-java-legacy',
start,
end: endLegacy,
apmApiClient,
useDurationSummary: true,
});

expect(res.body.currentPeriod.latencyTimeseries.map(({ y }) => y)).to.eql([
null,
null,
null,
null,
null,
null,
]);
});

it('has latency data for synth-java service', async () => {
const res = await getLatencyChartForService({
serviceName: 'synth-java',
start,
end: endLegacy,
apmApiClient,
useDurationSummary: true,
});

expect(res.body.currentPeriod.latencyTimeseries.map(({ y }) => y)).to.eql([
1000000, 1000000, 1000000, 1000000, 1000000, 1000000,
]);
});
});
});
}

// This will retrieve latency data expecting the `transaction.duration.summary` field to be present
function getLatencyChartForService({
serviceName,
start,
end,
apmApiClient,
useDurationSummary,
}: {
serviceName: string;
start: moment.Moment;
end: moment.Moment;
apmApiClient: ApmApiClient;
useDurationSummary: boolean;
}) {
return apmApiClient.readUser({
endpoint: `GET /internal/apm/services/{serviceName}/transactions/charts/latency`,
params: {
path: { serviceName },
query: {
start: start.toISOString(),
end: end.toISOString(),
environment: 'production',
latencyAggregationType: LatencyAggregationType.avg,
transactionType: 'request',
kuery: '',
documentType: ApmDocumentType.TransactionMetric,
rollupInterval: RollupInterval.OneMinute,
bucketSizeInSeconds: 60,
useDurationSummary,
},
},
});
}

function generateTraceDataForService({
serviceName,
start,
end,
isLegacy,
synthtrace,
}: {
serviceName: string;
start: moment.Moment;
end: moment.Moment;
isLegacy?: boolean;
synthtrace: ApmSynthtraceEsClient;
}) {
const instance = apm
.service({
name: serviceName,
environment: 'production',
agentName: 'java',
})
.instance(`instance`);

const events = timerange(start, end)
.ratePerMinute(6)
.generator((timestamp) =>
instance
.transaction({ transactionName: 'GET /order/{id}' })
.timestamp(timestamp)
.duration(1000)
.success()
);

const apmPipeline = (base: Readable) => {
return synthtrace.getDefaultPipeline({ versionOverride: '8.5.0' })(base);
};

return synthtrace.index(events, isLegacy ? apmPipeline : undefined);
}
Loading

0 comments on commit 8a49c1f

Please sign in to comment.