mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.12`: - [[APM] Add filter to `/has_data` api (#173382)](https://github.com/elastic/kibana/pull/173382) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Søren Louv-Jansen","email":"soren.louv@elastic.co"},"sourceCommit":{"committedDate":"2023-12-18T18:18:03Z","message":"[APM] Add filter to `/has_data` api (#173382)\n\nCloses https://github.com/elastic/kibana/issues/154997\r\n\r\nThis PR adds a data tier filter to the `/has_data` api, thus limitting\r\nthe number of shards being hit by the request.","sha":"e7593c0e46f1ce707c1b951f8d013e722ac79353","branchLabelMapping":{"^v8.13.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v8.12.0","v8.10.5","Team:obs-ux-infra_services","v8.13.0","v8.11.4"],"number":173382,"url":"https://github.com/elastic/kibana/pull/173382","mergeCommit":{"message":"[APM] Add filter to `/has_data` api (#173382)\n\nCloses https://github.com/elastic/kibana/issues/154997\r\n\r\nThis PR adds a data tier filter to the `/has_data` api, thus limitting\r\nthe number of shards being hit by the request.","sha":"e7593c0e46f1ce707c1b951f8d013e722ac79353"}},"sourceBranch":"main","suggestedTargetBranches":["8.12","8.10","8.11"],"targetPullRequestStates":[{"branch":"8.12","label":"v8.12.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.10","label":"v8.10.5","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.13.0","labelRegex":"^v8.13.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/173382","number":173382,"mergeCommit":{"message":"[APM] Add filter to `/has_data` api (#173382)\n\nCloses https://github.com/elastic/kibana/issues/154997\r\n\r\nThis PR adds a data tier filter to the `/has_data` api, thus limitting\r\nthe number of shards being hit by the request.","sha":"e7593c0e46f1ce707c1b951f8d013e722ac79353"}},{"branch":"8.11","label":"v8.11.4","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Søren Louv-Jansen <soren.louv@elastic.co>
This commit is contained in:
parent
34f6840f54
commit
23406d2a08
4 changed files with 58 additions and 420 deletions
|
@ -8,10 +8,29 @@
|
|||
import { ProcessorEvent } from '@kbn/observability-plugin/common';
|
||||
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
|
||||
|
||||
// Note: this logic is duplicated in tutorials/apm/envs/on_prem
|
||||
export async function hasHistoricalAgentData(apmEventClient: APMEventClient) {
|
||||
const hasDataInWarmOrHotDataTiers = await hasDataRequest(apmEventClient, [
|
||||
'data_hot',
|
||||
'data_warm',
|
||||
]);
|
||||
|
||||
if (hasDataInWarmOrHotDataTiers) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hasDataUnbounded = await hasDataRequest(apmEventClient);
|
||||
|
||||
return hasDataUnbounded;
|
||||
}
|
||||
|
||||
type DataTier = 'data_hot' | 'data_warm' | 'data_cold' | 'data_frozen';
|
||||
async function hasDataRequest(
|
||||
apmEventClient: APMEventClient,
|
||||
dataTiers?: DataTier[]
|
||||
) {
|
||||
const query = dataTiers ? { terms: { _tier: dataTiers } } : undefined;
|
||||
|
||||
const params = {
|
||||
terminate_after: 1,
|
||||
apm: {
|
||||
events: [
|
||||
ProcessorEvent.error,
|
||||
|
@ -20,8 +39,10 @@ export async function hasHistoricalAgentData(apmEventClient: APMEventClient) {
|
|||
],
|
||||
},
|
||||
body: {
|
||||
terminate_after: 1,
|
||||
track_total_hits: 1,
|
||||
size: 0,
|
||||
query,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,313 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`services queries fetches the agent status 1`] = `
|
||||
Object {
|
||||
"apm": Object {
|
||||
"events": Array [
|
||||
"error",
|
||||
"metric",
|
||||
"transaction",
|
||||
],
|
||||
},
|
||||
"body": Object {
|
||||
"size": 0,
|
||||
"track_total_hits": 1,
|
||||
},
|
||||
"terminate_after": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`services queries fetches the service agent name 1`] = `
|
||||
Object {
|
||||
"apm": Object {
|
||||
"events": Array [
|
||||
"error",
|
||||
"transaction",
|
||||
"metric",
|
||||
],
|
||||
},
|
||||
"body": Object {
|
||||
"_source": Array [
|
||||
"agent.name",
|
||||
"service.runtime.name",
|
||||
"cloud.provider",
|
||||
"cloud.service.name",
|
||||
],
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"term": Object {
|
||||
"service.name": "foo",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"range": Object {
|
||||
"@timestamp": Object {
|
||||
"format": "epoch_millis",
|
||||
"gte": 0,
|
||||
"lte": 50000,
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "agent.name",
|
||||
},
|
||||
},
|
||||
],
|
||||
"should": Array [
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "service.runtime.name",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "cloud.provider",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "cloud.service.name",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
"size": 1,
|
||||
"sort": Object {
|
||||
"_score": Object {
|
||||
"order": "desc",
|
||||
},
|
||||
},
|
||||
"track_total_hits": 1,
|
||||
},
|
||||
"terminate_after": 1,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`services queries fetches the service items 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"apm": Object {
|
||||
"sources": Array [
|
||||
Object {
|
||||
"documentType": "transactionEvent",
|
||||
"rollupInterval": "none",
|
||||
},
|
||||
],
|
||||
},
|
||||
"body": Object {
|
||||
"aggs": Object {
|
||||
"sample": Object {
|
||||
"aggs": Object {
|
||||
"overflowCount": Object {
|
||||
"sum": Object {
|
||||
"field": "service_transaction.aggregation.overflow_count",
|
||||
},
|
||||
},
|
||||
"services": Object {
|
||||
"aggs": Object {
|
||||
"transactionType": Object {
|
||||
"aggs": Object {
|
||||
"avg_duration": Object {
|
||||
"avg": Object {
|
||||
"field": "transaction.duration.us",
|
||||
},
|
||||
},
|
||||
"environments": Object {
|
||||
"terms": Object {
|
||||
"field": "service.environment",
|
||||
},
|
||||
},
|
||||
"sample": Object {
|
||||
"top_metrics": Object {
|
||||
"metrics": Array [
|
||||
Object {
|
||||
"field": "agent.name",
|
||||
},
|
||||
],
|
||||
"sort": Object {
|
||||
"@timestamp": "desc",
|
||||
},
|
||||
},
|
||||
},
|
||||
"successful": Object {
|
||||
"filter": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"terms": Object {
|
||||
"event.outcome": Array [
|
||||
"success",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"successful_or_failed": Object {
|
||||
"filter": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"terms": Object {
|
||||
"event.outcome": Array [
|
||||
"failure",
|
||||
"success",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"terms": Object {
|
||||
"field": "transaction.type",
|
||||
},
|
||||
},
|
||||
},
|
||||
"terms": Object {
|
||||
"field": "service.name",
|
||||
"size": 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
"random_sampler": Object {
|
||||
"probability": 1,
|
||||
"seed": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"@timestamp": Object {
|
||||
"format": "epoch_millis",
|
||||
"gte": 0,
|
||||
"lte": 50000,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
"size": 0,
|
||||
"track_total_hits": false,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"apm": Object {
|
||||
"events": Array [
|
||||
"metric",
|
||||
"error",
|
||||
],
|
||||
},
|
||||
"body": Object {
|
||||
"aggs": Object {
|
||||
"sample": Object {
|
||||
"aggs": Object {
|
||||
"services": Object {
|
||||
"aggs": Object {
|
||||
"environments": Object {
|
||||
"terms": Object {
|
||||
"field": "service.environment",
|
||||
},
|
||||
},
|
||||
"latest": Object {
|
||||
"top_metrics": Object {
|
||||
"metrics": Array [
|
||||
Object {
|
||||
"field": "agent.name",
|
||||
},
|
||||
],
|
||||
"sort": Object {
|
||||
"@timestamp": "desc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"terms": Object {
|
||||
"field": "service.name",
|
||||
"size": 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
"random_sampler": Object {
|
||||
"probability": 1,
|
||||
"seed": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"@timestamp": Object {
|
||||
"format": "epoch_millis",
|
||||
"gte": 0,
|
||||
"lte": 50000,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
"size": 0,
|
||||
"track_total_hits": false,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`services queries fetches the service transaction types 1`] = `
|
||||
Object {
|
||||
"apm": Object {
|
||||
"sources": Array [
|
||||
Object {
|
||||
"documentType": "transactionMetric",
|
||||
"rollupInterval": "1m",
|
||||
},
|
||||
],
|
||||
},
|
||||
"body": Object {
|
||||
"aggs": Object {
|
||||
"types": Object {
|
||||
"terms": Object {
|
||||
"field": "transaction.type",
|
||||
"size": 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"term": Object {
|
||||
"service.name": "foo",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"range": Object {
|
||||
"@timestamp": Object {
|
||||
"format": "epoch_millis",
|
||||
"gte": 0,
|
||||
"lte": 50000,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
"size": 0,
|
||||
"track_total_hits": false,
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -1,90 +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 { ApmDocumentType } from '../../../common/document_type';
|
||||
import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values';
|
||||
import { RollupInterval } from '../../../common/rollup';
|
||||
import {
|
||||
inspectSearchParams,
|
||||
SearchParamsMock,
|
||||
} from '../../utils/test_helpers';
|
||||
import { hasHistoricalAgentData } from '../historical_data/has_historical_agent_data';
|
||||
import { getServicesItems } from './get_services/get_services_items';
|
||||
import { getServiceAgent } from './get_service_agent';
|
||||
import { getServiceTransactionTypes } from './get_service_transaction_types';
|
||||
|
||||
describe('services queries', () => {
|
||||
let mock: SearchParamsMock;
|
||||
|
||||
afterEach(() => {
|
||||
mock.teardown();
|
||||
});
|
||||
|
||||
it('fetches the service agent name', async () => {
|
||||
mock = await inspectSearchParams(({ mockApmEventClient }) =>
|
||||
getServiceAgent({
|
||||
serviceName: 'foo',
|
||||
apmEventClient: mockApmEventClient,
|
||||
start: 0,
|
||||
end: 50000,
|
||||
})
|
||||
);
|
||||
|
||||
expect(mock.params).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fetches the service transaction types', async () => {
|
||||
mock = await inspectSearchParams(({ mockApmEventClient }) =>
|
||||
getServiceTransactionTypes({
|
||||
serviceName: 'foo',
|
||||
apmEventClient: mockApmEventClient,
|
||||
start: 0,
|
||||
end: 50000,
|
||||
documentType: ApmDocumentType.TransactionMetric,
|
||||
rollupInterval: RollupInterval.OneMinute,
|
||||
})
|
||||
);
|
||||
|
||||
expect(mock.params).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fetches the service items', async () => {
|
||||
mock = await inspectSearchParams(
|
||||
({ mockApmEventClient, mockApmAlertsClient }) =>
|
||||
getServicesItems({
|
||||
mlClient: undefined,
|
||||
apmEventClient: mockApmEventClient,
|
||||
documentType: ApmDocumentType.TransactionEvent,
|
||||
rollupInterval: RollupInterval.None,
|
||||
logger: {} as any,
|
||||
environment: ENVIRONMENT_ALL.value,
|
||||
kuery: '',
|
||||
start: 0,
|
||||
end: 50000,
|
||||
serviceGroup: null,
|
||||
randomSampler: {
|
||||
probability: 1,
|
||||
seed: 0,
|
||||
},
|
||||
apmAlertsClient: mockApmAlertsClient,
|
||||
useDurationSummary: false,
|
||||
})
|
||||
);
|
||||
|
||||
const allParams = mock.spy.mock.calls.map((call) => call[1]);
|
||||
|
||||
expect(allParams).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fetches the agent status', async () => {
|
||||
mock = await inspectSearchParams(({ mockApmEventClient }) =>
|
||||
hasHistoricalAgentData(mockApmEventClient)
|
||||
);
|
||||
|
||||
expect(mock.params).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -6,34 +6,54 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import moment from 'moment';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const registry = getService('registry');
|
||||
const apmApiClient = getService('apmApiClient');
|
||||
const archiveName = 'apm_8.0.0';
|
||||
const synthtraceEsClient = getService('synthtraceEsClient');
|
||||
|
||||
registry.when(
|
||||
'Historical data when data is not loaded',
|
||||
{ config: 'basic', archives: [] },
|
||||
() => {
|
||||
it('handles the empty state', async () => {
|
||||
registry.when('Historical data ', { config: 'basic', archives: [] }, () => {
|
||||
describe('when there is not data', () => {
|
||||
it('returns hasData=false', async () => {
|
||||
const response = await apmApiClient.readUser({ endpoint: `GET /internal/apm/has_data` });
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.hasData).to.be(false);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
registry.when(
|
||||
'Historical data when data is loaded',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
() => {
|
||||
it('returns hasData: true', async () => {
|
||||
describe('when there is data', () => {
|
||||
before(async () => {
|
||||
const start = moment().subtract(30, 'minutes').valueOf();
|
||||
const end = moment().valueOf();
|
||||
|
||||
const serviceInstance = apm
|
||||
.service({ name: 'my-go-service', environment: 'production', agentName: 'go' })
|
||||
.instance('instance-a');
|
||||
|
||||
const documents = [
|
||||
timerange(start, end)
|
||||
.interval('1m')
|
||||
.generator((timestamp) =>
|
||||
serviceInstance
|
||||
.transaction({ transactionName: 'GET /users' })
|
||||
.timestamp(timestamp)
|
||||
.duration(10)
|
||||
),
|
||||
];
|
||||
|
||||
await synthtraceEsClient.index(documents);
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
it('returns hasData=true', async () => {
|
||||
const response = await apmApiClient.readUser({ endpoint: `GET /internal/apm/has_data` });
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.hasData).to.be(true);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue