[apm] allow retrieval of metric indices (#167041)

### Summary

Closes https://github.com/elastic/kibana/issues/166961

`/internal/apm/services/{serviceName}/infrastructure_attributes` route
was disabled in serverless as it relied on an infra API to function.
Since the infra plugin dependency was removed in
https://github.com/elastic/kibana/pull/164094 we can reenable the route

### Testing
I used a ccs cluster connected to edge-oblt and had to update the apm
indices to also search the remote_cluster
```
xpack.apm.indices.metric: remote_cluster:metrics-apm*,remote_cluster:apm*,metrics-apm*,apm*
xpack.apm.indices.transaction: remote_cluster:traces-apm*,remote_cluster:apm*,traces-apm*,apm*
xpack.apm.indices.span: remote_cluster:traces-apm*,remote_cluster:apm*,traces-apm*,apm*
xpack.apm.indices.error: remote_cluster:logs-apm*,remote_cluster:apm*,logs-apm*,apm*
```
- start serverless kibana
- navigate to Applications -> Services, we need to select a [service
linked to a
container](https://github.com/elastic/kibana/blob/main/x-pack/plugins/apm/server/routes/infrastructure/get_host_names.ts#L23)
to fully trigger the route logic (you can pick `quoteservice` if
connected to edge-oblt data)
- navigate to Logs tab
- call to `/infrastructure_attributes` is successful
This commit is contained in:
Kevin Lacabane 2023-09-25 18:28:09 +02:00 committed by GitHub
parent 2bce7bbcbe
commit 88fdebdc81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 24 additions and 48 deletions

View file

@ -6,7 +6,7 @@
*/
import { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types';
import { APMRouteHandlerResources } from '../../../../routes/typings';
import { APMRouteHandlerResources } from '../../../../routes/apm_routes/register_apm_server_routes';
type InfraMetricsSearchParams = Omit<ESSearchRequest, 'index'> & {
size: number;

View file

@ -1,25 +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 { SavedObjectsClientContract } from '@kbn/core/server';
import { APMRouteHandlerResources } from '../../routes/apm_routes/register_apm_server_routes';
export async function getInfraMetricIndices({
infraPlugin,
savedObjectsClient,
}: {
infraPlugin: Required<APMRouteHandlerResources['plugins']['infra']>;
savedObjectsClient: SavedObjectsClientContract;
}): Promise<string> {
if (!infraPlugin) {
throw new Error('Infra Plugin needs to be setup');
}
const infra = await infraPlugin.start();
const infraMetricIndices = await infra.getMetricIndices(savedObjectsClient);
return infraMetricIndices;
}

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import * as t from 'io-ts';
import Boom from '@hapi/boom';
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
import { getApmEventClient } from '../../lib/helpers/get_apm_event_client';
import { environmentRt, kueryRt, rangeRt } from '../default_api_types';
@ -30,10 +29,6 @@ const infrastructureRoute = createApmServerRoute({
hostNames: string[];
podNames: string[];
}> => {
if (!resources.plugins.infra) {
throw Boom.notFound();
}
const apmEventClient = await getApmEventClient(resources);
const infraMetricsClient = createInfraMetricsClient(resources);
const { params } = resources;

View file

@ -250,7 +250,7 @@ const serviceMetadataDetailsRoute = createApmServerRoute({
end,
});
if (serviceMetadataDetails?.container?.ids && resources.plugins.infra) {
if (serviceMetadataDetails?.container?.ids) {
const infraMetricsClient = createInfraMetricsClient(resources);
const containerMetadata = await getServiceOverviewContainerMetadata({
infraMetricsClient,
@ -761,10 +761,7 @@ export const serviceInstancesMetadataDetails = createApmServerRoute({
end,
});
if (
serviceInstanceMetadataDetails?.container?.id &&
resources.plugins.infra
) {
if (serviceInstanceMetadataDetails?.container?.id) {
const infraMetricsClient = createInfraMetricsClient(resources);
const containerMetadata = await getServiceInstanceContainerMetadata({
infraMetricsClient,

View file

@ -7,18 +7,13 @@
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import { MetricsDataClient } from './client';
import { MetricsDataClient, DEFAULT_METRIC_INDICES } from './client';
import { metricsDataSourceSavedObjectName } from '../saved_objects/metrics_data_source';
describe('MetricsDataClient', () => {
const client = new MetricsDataClient();
client.setDefaultMetricIndicesHandler(async () => {
return 'fallback-indices*';
});
describe('metric indices', () => {
it('retrieves metrics saved object', async () => {
const client = new MetricsDataClient();
const savedObjectsClient = {
get: jest.fn().mockResolvedValue({ attributes: { metricIndices: 'foo,bar' } }),
};
@ -36,6 +31,10 @@ describe('MetricsDataClient', () => {
});
it('falls back to provided handler when no metrics saved object exists', async () => {
const client = new MetricsDataClient();
client.setDefaultMetricIndicesHandler(async () => {
return 'fallback-indices*';
});
const savedObjectsClient = {
get: jest.fn().mockRejectedValue(SavedObjectsErrorHelpers.createGenericNotFoundError()),
};
@ -51,5 +50,17 @@ describe('MetricsDataClient', () => {
]);
expect(indices).toEqual('fallback-indices*');
});
it('falls back to static indices when no fallback exists', async () => {
const client = new MetricsDataClient();
const savedObjectsClient = {
get: jest.fn().mockRejectedValue(SavedObjectsErrorHelpers.createGenericNotFoundError()),
};
const indices = await client.getMetricIndices({
savedObjectsClient: savedObjectsClient as unknown as SavedObjectsClientContract,
});
expect(indices).toEqual(DEFAULT_METRIC_INDICES);
});
});
});

View file

@ -16,21 +16,19 @@ import {
metricsDataSourceSavedObjectName,
} from '../saved_objects/metrics_data_source';
export const DEFAULT_METRIC_INDICES = 'metrics-*,metricbeat-*';
export class MetricsDataClient {
private readonly defaultSavedObjectId = 'default';
private getDefaultMetricIndices: DefaultMetricIndicesHandler = null;
async getMetricIndices(options: GetMetricIndicesOptions): Promise<string> {
if (!this.getDefaultMetricIndices) {
throw new Error('Missing getMetricsIndices fallback');
}
const metricIndices = await options.savedObjectsClient
.get<MetricsDataSavedObject>(metricsDataSourceSavedObjectName, this.defaultSavedObjectId)
.then(({ attributes }) => attributes.metricIndices)
.catch((err) => {
if (SavedObjectsErrorHelpers.isNotFoundError(err)) {
return this.getDefaultMetricIndices!(options);
return this.getDefaultMetricIndices?.(options) ?? DEFAULT_METRIC_INDICES;
}
throw err;