[Discover][APM] Refactor APM sources for better trace enablement (#212634)

## Summary

This PR reworks how APM handles getting its sources data, elevating the
necessary code to a private shared plugin so that Discover for Traces
can access the data and handle user provided configuration. It also
removes the need for Discover for Traces to rely on the APM static data
view, so the Trace data source and document profile will work on any
compatible/configured index, even in ESQL mode.

Closes #211414

<img alt="ESQL Discover Traces Screenshot 2025-03-04 173032"
src="https://github.com/user-attachments/assets/f5bbb736-8b8b-45dc-ac23-4bf7083aa47e"
/>

## How to test

Test with olbt-cli instance for now, will post for doing with synthtrace
data. Ensure the following is added to your kibana.dev.yml:

```yaml
discover.experimental.enabledProfiles:
  - observability-traces-data-source-profile
```

- Make sure your space has the Observability solution view configured
- Go to Discover page
- Select Data Views mode if required and create a view with a `traces`
specific index. Or use the APM static data view.
- The default columns on the page should show the summary column with
four of the following badges: `service.name`, `event.outcome`,
`transaction.name`, `transaction.duration.us`, `span.name`,
`span.duration.us`
- Go to ESQL mode with the query targetting a `traces` index
- The default columns should show the same as in Data View mode

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Irene Blanco <irene.blanco@elastic.co>
This commit is contained in:
Gonçalo Rica Pais da Silva 2025-03-19 15:52:30 +01:00 committed by GitHub
parent 2f453ac2b7
commit 54d9cf45cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
106 changed files with 1070 additions and 572 deletions

1
.github/CODEOWNERS vendored
View file

@ -888,6 +888,7 @@ x-pack/platform/plugins/shared/ai_infra/llm_tasks @elastic/appex-ai-infra
x-pack/platform/plugins/shared/ai_infra/product_doc_base @elastic/appex-ai-infra
x-pack/platform/plugins/shared/aiops @elastic/ml-ui
x-pack/platform/plugins/shared/alerting @elastic/response-ops
x-pack/platform/plugins/shared/apm_sources_access @elastic/obs-ux-infra_services-team
x-pack/platform/plugins/shared/automatic_import @elastic/security-scalability
x-pack/platform/plugins/shared/cases @elastic/response-ops
x-pack/platform/plugins/shared/cloud @elastic/kibana-core

View file

@ -197,6 +197,7 @@
"@kbn/apm-data-access-plugin": "link:x-pack/solutions/observability/plugins/apm_data_access",
"@kbn/apm-data-view": "link:src/platform/packages/shared/kbn-apm-data-view",
"@kbn/apm-plugin": "link:x-pack/solutions/observability/plugins/apm",
"@kbn/apm-sources-access-plugin": "link:x-pack/platform/plugins/shared/apm_sources_access",
"@kbn/apm-types": "link:x-pack/platform/packages/shared/kbn-apm-types",
"@kbn/apm-utils": "link:src/platform/packages/shared/kbn-apm-utils",
"@kbn/app-link-test-plugin": "link:src/platform/test/plugin_functional/plugins/app_link_test",

View file

@ -5,6 +5,7 @@ pageLoadAssetSize:
aiops: 32733
alerting: 106936
apm: 64385
apmSourcesAccess: 16765
automaticImport: 25433
banners: 17946
canvas: 29355

View file

@ -30,6 +30,7 @@ export {
buildDataTableRecord,
buildDataTableRecordList,
createLogsContextService,
createTracesContextService,
createDegradedDocsControl,
createStacktraceControl,
fieldConstants,
@ -58,7 +59,7 @@ export {
LogLevelBadge,
} from './src';
export type { LogsContextService } from './src';
export type { LogsContextService, TracesContextService } from './src';
export * from './src/types';

View file

@ -11,3 +11,4 @@ export * from './data_view';
export * from './es_hits';
export * from './additional_field_groups';
export * from './logs_context_service';
export * from './traces_context_service';

View file

@ -0,0 +1,16 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import {
DEFAULT_ALLOWED_TRACES_BASE_PATTERNS_REGEXP,
getTracesContextService,
} from '../data_types';
export const createTracesContextServiceMock = () =>
getTracesContextService('traces-*', [DEFAULT_ALLOWED_TRACES_BASE_PATTERNS_REGEXP]);

View file

@ -37,24 +37,17 @@ export const createLogsContextService = async ({
logsDataAccess,
}: LogsContextServiceDeps): Promise<LogsContextService> => {
let allLogsIndexPattern: string | undefined;
let logSources: string[] | undefined;
let allowedDataSources: Array<string | RegExp> = [DEFAULT_ALLOWED_LOGS_BASE_PATTERNS_REGEXP];
if (logsDataAccess) {
const logSourcesService = logsDataAccess.services.logSourcesService;
allLogsIndexPattern = (await logSourcesService.getLogSources())
.map((logSource) => logSource.indexPattern)
.join(','); // TODO: Will be replaced by helper in: https://github.com/elastic/kibana/pull/192003
logSources = allLogsIndexPattern.split(',');
allLogsIndexPattern = await logSourcesService.getFlattenedLogSources();
allowedDataSources = allowedDataSources.concat(allLogsIndexPattern.split(','));
}
const ALLOWED_LOGS_DATA_SOURCES = [
DEFAULT_ALLOWED_LOGS_BASE_PATTERNS_REGEXP,
...(logSources ? logSources : []),
];
return getLogsContextService({
allLogsIndexPattern,
allowedDataSources: ALLOWED_LOGS_DATA_SOURCES,
allowedDataSources,
});
};
@ -66,12 +59,10 @@ export const getLogsContextService = ({
allowedDataSources: Array<string | RegExp>;
}): LogsContextService => {
const getAllLogsIndexPattern = () => allLogsIndexPattern;
const isLogsIndexPattern = (indexPattern: unknown) => {
return (
typeof indexPattern === 'string' &&
testPatternAgainstAllowedList(allowedDataSources)(indexPattern)
);
};
const isLogsIndexPattern = testPatternAgainstAllowedList(allowedDataSources);
return { getAllLogsIndexPattern, isLogsIndexPattern };
return {
getAllLogsIndexPattern,
isLogsIndexPattern,
};
};

View file

@ -8,3 +8,4 @@
*/
export * from './types';
export * from './traces_context_service';

View file

@ -0,0 +1,73 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { ApmSourceAccessPluginStart } from '@kbn/apm-sources-access-plugin/public';
import { createRegExpPatternFrom, testPatternAgainstAllowedList } from '@kbn/data-view-utils';
import { containsIndexPattern } from '../../utils';
export interface TracesContextService {
getAllTracesIndexPattern(): string | undefined;
isTracesIndexPattern(indexPattern: unknown): boolean;
containsTracesIndexPattern(indexPattern: unknown): boolean;
}
export interface TracesContextServiceDeps {
apmSourcesAccess?: ApmSourceAccessPluginStart;
}
export const DEFAULT_ALLOWED_TRACES_BASE_PATTERNS = ['trace', 'traces'];
export const DEFAULT_ALLOWED_TRACES_BASE_PATTERNS_REGEXP = createRegExpPatternFrom(
DEFAULT_ALLOWED_TRACES_BASE_PATTERNS,
'data'
);
export const createTracesContextService = async ({
apmSourcesAccess,
}: TracesContextServiceDeps): Promise<TracesContextService> => {
if (!apmSourcesAccess) {
return defaultTracesContextService;
}
try {
const indices = await apmSourcesAccess.getApmIndices();
if (!indices) {
return defaultTracesContextService;
}
const { transaction, span } = indices;
const allIndices = getAllIndices(transaction, span);
const uniqueIndices = Array.from(new Set(allIndices));
const traces = uniqueIndices.join();
const allowedDataSources = [createRegExpPatternFrom(uniqueIndices, 'data')];
return getTracesContextService(traces, allowedDataSources);
} catch (error) {
return defaultTracesContextService;
}
};
function getAllIndices(transaction: string, span: string) {
return [transaction, span]
.flatMap((index) => index.split(','))
.concat(DEFAULT_ALLOWED_TRACES_BASE_PATTERNS);
}
export const getTracesContextService = (traces: string, allowedDataSources: RegExp[]) => ({
getAllTracesIndexPattern: () => traces,
isTracesIndexPattern: testPatternAgainstAllowedList(allowedDataSources),
containsTracesIndexPattern: containsIndexPattern(allowedDataSources),
});
const defaultTracesContextService = getTracesContextService(
DEFAULT_ALLOWED_TRACES_BASE_PATTERNS.join(),
[DEFAULT_ALLOWED_TRACES_BASE_PATTERNS_REGEXP]
);

View file

@ -0,0 +1,35 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { DEFAULT_ALLOWED_TRACES_BASE_PATTERNS_REGEXP } from '../data_types/traces/traces_context_service';
import { containsIndexPattern } from './contains_index_pattern';
describe('containsIndexPattern', () => {
const allowed = [DEFAULT_ALLOWED_TRACES_BASE_PATTERNS_REGEXP, 'custom-*'];
const isTraceIndex = containsIndexPattern(allowed);
const testCases: Array<[string, boolean]> = [
['traces-*', true],
['logs-*', false],
['custom-*', true],
['otel-*,apm-*,traces-apm*,traces-*.otel-*', true],
[
'remote_cluster:apm-*,remote_cluster:traces-apm*,remote_cluster:traces-*.otel-*,apm-*,traces-apm*,traces-*.otel-*',
true,
],
[
'remote_cluster:filebeat-*,remote_cluster:logs-*,remote_cluster:kibana_sample_data_logs*,filebeat-*,kibana_sample_data_logs*,logs-*',
false,
],
];
it.each(testCases)('Evaluates a traces index "%s" as %p', (index, expected) => {
expect(isTraceIndex(index)).toBe(expected);
});
});

View file

@ -0,0 +1,19 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export const containsIndexPattern =
(allowedDataSources: Array<string | RegExp>) => (indexPattern: unknown) =>
typeof indexPattern === 'string' &&
indexPattern
.split(',')
.some((index) =>
allowedDataSources.some((pattern) =>
typeof pattern === 'string' ? index === pattern : pattern.test(index)
)
);

View file

@ -9,6 +9,7 @@
export * from './build_data_record';
export * from './calc_field_counts';
export * from './contains_index_pattern';
export * from './format_hit';
export * from './format_value';
export * from './get_doc_id';
@ -20,6 +21,5 @@ export * from './get_should_show_field_handler';
export * from './get_stack_trace_fields';
export * from './nested_fields';
export * from './get_field_value';
export * from './calc_field_counts';
export * from './get_visible_columns';
export { DiscoverFlyouts, dismissAllFlyoutsExceptFor, dismissFlyouts } from './dismiss_flyouts';

View file

@ -23,6 +23,7 @@
"@kbn/expressions-plugin",
"@kbn/logs-data-access-plugin",
"@kbn/i18n-react",
"@kbn/navigation-plugin"
"@kbn/navigation-plugin",
"@kbn/apm-sources-access-plugin",
]
}

View file

@ -49,7 +49,8 @@
"aiops",
"fieldsMetadata",
"logsDataAccess",
"embeddableEnhanced"
"embeddableEnhanced",
"apmSourcesAccess"
],
"requiredBundles": [
"kibanaUtils",
@ -61,4 +62,4 @@
"common"
]
}
}
}

View file

@ -61,6 +61,7 @@ import type { AiopsPluginStart } from '@kbn/aiops-plugin/public';
import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public';
import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public';
import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public';
import type { ApmSourceAccessPluginStart } from '@kbn/apm-sources-access-plugin/public';
import type { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public';
import type { EmbeddableEnhancedPluginStart } from '@kbn/embeddable-enhanced-plugin/public';
import type { DiscoverStartPlugins } from './types';
@ -142,6 +143,7 @@ export interface DiscoverServices {
fieldsMetadata?: FieldsMetadataPublicStart;
logsDataAccess?: LogsDataAccessPluginStart;
embeddableEnhanced?: EmbeddableEnhancedPluginStart;
apmSourcesAccess?: ApmSourceAccessPluginStart;
}
export const buildServices = ({
@ -235,5 +237,6 @@ export const buildServices = ({
fieldsMetadata: plugins.fieldsMetadata,
logsDataAccess: plugins.logsDataAccess,
embeddableEnhanced: plugins.embeddableEnhanced,
apmSourcesAccess: plugins.apmSourcesAccess,
};
};

View file

@ -26,7 +26,10 @@ import {
import type { ProfileProviderServices } from '../profile_providers/profile_provider_services';
import { ProfilesManager } from '../profiles_manager';
import { DiscoverEBTManager } from '../../plugin_imports/discover_ebt_manager';
import { createLogsContextServiceMock } from '@kbn/discover-utils/src/__mocks__';
import {
createLogsContextServiceMock,
createTracesContextServiceMock,
} from '@kbn/discover-utils/src/__mocks__';
import { discoverSharedPluginMock } from '@kbn/discover-shared-plugin/public/mocks';
export const createContextAwarenessMocks = ({
@ -189,5 +192,6 @@ const createProfileProviderServicesMock = () => {
return {
logsContextService: createLogsContextServiceMock(),
discoverShared: discoverSharedPluginMock.createStartContract(),
tracesContextService: createTracesContextServiceMock(),
} as ProfileProviderServices;
};

View file

@ -8,13 +8,22 @@
*/
import type { DataView } from '@kbn/data-views-plugin/common';
import type { DataSourceProfileProviderParams } from '../../../profiles';
import { DataSourceCategory } from '../../../profiles';
import {
DataSourceCategory,
type DataSourceProfileProviderParams,
SolutionType,
type RootContext,
} from '../../../profiles';
import { DataSourceType, createDataViewDataSource } from '../../../../../common/data_sources';
import { createTracesDataSourceProfileProvider } from './profile';
import { createContextAwarenessMocks } from '../../../__mocks__';
import type { ContextWithProfileId } from '../../../profile_service';
import { OBSERVABILITY_ROOT_PROFILE_ID } from '../consts';
const mockServices = createContextAwarenessMocks().profileProviderServices;
describe('tracesDataSourceProfileProvider', () => {
const tracesDataSourceProfileProvider = createTracesDataSourceProfileProvider();
const tracesDataSourceProfileProvider = createTracesDataSourceProfileProvider(mockServices);
const RESOLUTION_MATCH = {
isMatch: true,
@ -25,9 +34,15 @@ describe('tracesDataSourceProfileProvider', () => {
isMatch: false,
};
it('should match when the data source type is a data view for APM', () => {
const ROOT_CONTEXT: ContextWithProfileId<RootContext> = {
profileId: OBSERVABILITY_ROOT_PROFILE_ID,
solutionType: SolutionType.Observability,
};
it('should match when the index is for traces', () => {
expect(
tracesDataSourceProfileProvider.resolve({
rootContext: ROOT_CONTEXT,
dataSource: createDataViewDataSource({ dataViewId: 'apm_static_data_view_id_default' }),
dataView: {
getIndexPattern: () => 'traces-*',
@ -37,24 +52,35 @@ describe('tracesDataSourceProfileProvider', () => {
expect(
tracesDataSourceProfileProvider.resolve({
rootContext: ROOT_CONTEXT,
dataSource: createDataViewDataSource({ dataViewId: 'apm_static_data_view_id_custom_view' }),
dataView: {
getIndexPattern: () => 'traces-*',
} as unknown as DataView,
} as DataSourceProfileProviderParams)
).toEqual(RESOLUTION_MATCH);
});
it('should NOT match when the data source is not the APM data view', () => {
expect(
tracesDataSourceProfileProvider.resolve({
rootContext: ROOT_CONTEXT,
dataSource: createDataViewDataSource({ dataViewId: 'other_view_id' }),
dataView: { getIndexPattern: () => 'traces-*' } as unknown as DataView,
} as DataSourceProfileProviderParams)
).toEqual(RESOLUTION_MATCH);
expect(
tracesDataSourceProfileProvider.resolve({
rootContext: ROOT_CONTEXT,
dataSource: { type: DataSourceType.Esql },
query: { esql: 'FROM traces' },
} as DataSourceProfileProviderParams)
).toEqual(RESOLUTION_MISMATCH);
).toEqual(RESOLUTION_MATCH);
});
it('should NOT match when the index is not for traces', () => {
expect(
tracesDataSourceProfileProvider.resolve({
rootContext: ROOT_CONTEXT,
dataSource: { type: DataSourceType.Esql },
query: { esql: 'FROM logs' },
} as DataSourceProfileProviderParams)
@ -62,9 +88,23 @@ describe('tracesDataSourceProfileProvider', () => {
expect(
tracesDataSourceProfileProvider.resolve({
dataSource: createDataViewDataSource({ dataViewId: 'other_view_id' }),
rootContext: ROOT_CONTEXT,
dataSource: createDataViewDataSource({ dataViewId: 'other_logs_view_id' }),
dataView: { getIndexPattern: () => 'logs-*' } as unknown as DataView,
} as DataSourceProfileProviderParams)
).toEqual(RESOLUTION_MISMATCH);
});
it("should NOT match when the root context isn't Observability", () => {
expect(
tracesDataSourceProfileProvider.resolve({
rootContext: {
profileId: 'security-root-profile',
solutionType: SolutionType.Security,
},
dataSource: createDataViewDataSource({ dataViewId: 'other_view_id' }),
dataView: { getIndexPattern: () => 'traces-*' } as unknown as DataView,
} as DataSourceProfileProviderParams)
).toEqual(RESOLUTION_MISMATCH);
});
});

View file

@ -7,17 +7,22 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { DataSourceType, isDataSourceType } from '../../../../../common/data_sources';
import { DataSourceCategory, type DataSourceProfileProvider } from '../../../profiles';
import { extractIndexPatternFrom } from '../../extract_index_pattern_from';
import type { ProfileProviderServices } from '../../profile_provider_services';
import { getCellRenderers } from './accessors';
import { OBSERVABILITY_ROOT_PROFILE_ID } from '../consts';
const OBSERVABILITY_TRACES_DATA_SOURCE_PROFILE_ID = 'observability-traces-data-source-profile';
export const createTracesDataSourceProfileProvider = (): DataSourceProfileProvider => ({
export const createTracesDataSourceProfileProvider = ({
tracesContextService,
}: ProfileProviderServices): DataSourceProfileProvider => ({
profileId: OBSERVABILITY_TRACES_DATA_SOURCE_PROFILE_ID,
isExperimental: true,
profile: {
getDefaultAppState: () => () => ({
getDefaultAppState: (prev) => (params) => ({
...prev(params),
columns: [
{
name: '@timestamp',
@ -31,10 +36,10 @@ export const createTracesDataSourceProfileProvider = (): DataSourceProfileProvid
}),
getCellRenderers,
},
resolve: ({ dataSource }) => {
resolve: (params) => {
if (
isDataSourceType(dataSource, DataSourceType.DataView) &&
dataSource.dataViewId.includes('apm_static_data_view_id')
params.rootContext.profileId === OBSERVABILITY_ROOT_PROFILE_ID &&
tracesContextService.containsTracesIndexPattern(extractIndexPatternFrom(params))
) {
return {
isMatch: true,

View file

@ -7,9 +7,15 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { LogsContextService } from '@kbn/discover-utils';
import { createLogsContextService } from '@kbn/discover-utils';
import {
createLogsContextService,
type LogsContextService,
createTracesContextService,
type TracesContextService,
} from '@kbn/discover-utils';
import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public';
import type { ApmSourceAccessPluginStart } from '@kbn/apm-sources-access-plugin/public';
import type { DiscoverServices } from '../../build_services';
/**
@ -17,6 +23,7 @@ import type { DiscoverServices } from '../../build_services';
*/
export interface ProfileProviderDeps extends DiscoverServices {
logsDataAccess?: LogsDataAccessPluginStart;
apmSourcesAccess?: ApmSourceAccessPluginStart;
}
/**
@ -27,6 +34,7 @@ export interface ProfileProviderServices extends DiscoverServices {
* A service containing methods used for logs profiles
*/
logsContextService: LogsContextService;
tracesContextService: TracesContextService;
}
/**
@ -42,5 +50,8 @@ export const createProfileProviderServices = async (
logsContextService: await createLogsContextService({
logsDataAccess: discoverServices.logsDataAccess,
}),
tracesContextService: await createTracesContextService({
apmSourcesAccess: discoverServices.apmSourcesAccess,
}),
};
};

View file

@ -138,7 +138,7 @@ const createRootProfileProviders = (providerServices: ProfileProviderServices) =
const createDataSourceProfileProviders = (providerServices: ProfileProviderServices) => [
createExampleDataSourceProfileProvider(),
createDeprecationLogsDataSourceProfileProvider(),
createTracesDataSourceProfileProvider(),
createTracesDataSourceProfileProvider(providerServices),
...createObservabilityLogsDataSourceProfileProviders(providerServices),
];

View file

@ -44,6 +44,7 @@ import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/publ
import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public';
import type { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public';
import type { EmbeddableEnhancedPluginStart } from '@kbn/embeddable-enhanced-plugin/public';
import type { ApmSourceAccessPluginStart } from '@kbn/apm-sources-access-plugin/public';
import type { DiscoverAppLocator } from '../common';
import type { DiscoverContainerProps } from './components/discover_container';
@ -175,4 +176,5 @@ export interface DiscoverStartPlugins {
urlForwarding: UrlForwardingStart;
usageCollection?: UsageCollectionSetup;
embeddableEnhanced?: EmbeddableEnhancedPluginStart;
apmSourcesAccess?: ApmSourceAccessPluginStart;
}

View file

@ -96,6 +96,7 @@
"@kbn/fields-metadata-plugin",
"@kbn/discover-contextual-components",
"@kbn/logs-data-access-plugin",
"@kbn/apm-sources-access-plugin",
"@kbn/core-lifecycle-browser",
"@kbn/esql-ast",
"@kbn/discover-shared-plugin",

View file

@ -90,6 +90,8 @@
"@kbn/apm-ftr-e2e/*": ["x-pack/solutions/observability/plugins/apm/ftr_e2e/*"],
"@kbn/apm-plugin": ["x-pack/solutions/observability/plugins/apm"],
"@kbn/apm-plugin/*": ["x-pack/solutions/observability/plugins/apm/*"],
"@kbn/apm-sources-access-plugin": ["x-pack/platform/plugins/shared/apm_sources_access"],
"@kbn/apm-sources-access-plugin/*": ["x-pack/platform/plugins/shared/apm_sources_access/*"],
"@kbn/apm-synthtrace": ["src/platform/packages/shared/kbn-apm-synthtrace"],
"@kbn/apm-synthtrace/*": ["src/platform/packages/shared/kbn-apm-synthtrace/*"],
"@kbn/apm-synthtrace-client": ["src/platform/packages/shared/kbn-apm-synthtrace-client"],

View file

@ -15,7 +15,7 @@
"xpack.stackAlerts": "platform/plugins/shared/stack_alerts",
"xpack.stackConnectors": "platform/plugins/shared/stack_connectors",
"xpack.apm": "solutions/observability/plugins/apm",
"xpack.apmDataAccess": "solutions/observability/plugins/apm_data_access",
"xpack.apmSourcesAccess": "platform/plugins/shared/apm_sources_access",
"xpack.banners": "platform/plugins/private/banners",
"xpack.canvas": "platform/plugins/private/canvas",
"xpack.cases": "platform/plugins/shared/cases",

View file

@ -12719,7 +12719,7 @@
"xpack.apm.welcome.image.alt": "Image de la nouvelle expérience de l'inventaire des services, montrant les services détectés à partir des logs ainsi que les services instrumentés par APM",
"xpack.apm.welcome.linkLabel": "En savoir plus",
"xpack.apm.welcome.title": "Bienvenue dans notre nouvelle expérience !",
"xpack.apmDataAccess.apmSettings.index": "Paramètres APM - Index",
"xpack.apmSourcesAccess.apmSettings.index": "Paramètres APM - Index",
"xpack.banners.settings.backgroundColor.description": "Définissez la couleur d'arrière-plan de la bannière. {subscriptionLink}",
"xpack.banners.settings.backgroundColor.title": "Couleur d'arrière-plan de la bannière",
"xpack.banners.settings.placement.description": "Affichez une bannière haute au-dessus de l'en-tête Elastic. {subscriptionLink}",

View file

@ -12705,7 +12705,7 @@
"xpack.apm.welcome.image.alt": "ログから検出されたサービスとAPMで測定されたサービスを表示する、サービスインベントリの新しいエクスペリエンスの図",
"xpack.apm.welcome.linkLabel": "詳細",
"xpack.apm.welcome.title": "新しいエクスペリエンスへようこそ!",
"xpack.apmDataAccess.apmSettings.index": "APM 設定 - インデックス",
"xpack.apmSourcesAccess.apmSettings.index": "APM 設定 - インデックス",
"xpack.banners.settings.backgroundColor.description": "バナーの背景色。{subscriptionLink}",
"xpack.banners.settings.backgroundColor.title": "バナー背景色",
"xpack.banners.settings.placement.description": "Elasticヘッダーの上に、上部のバナーを表示します。{subscriptionLink}",

View file

@ -12732,7 +12732,7 @@
"xpack.apm.welcome.image.alt": "服务库存新体验的图像,显示从日志中检测到的服务和 APM-instrumented 服务",
"xpack.apm.welcome.linkLabel": "了解详情",
"xpack.apm.welcome.title": "欢迎参与我们的新体验!",
"xpack.apmDataAccess.apmSettings.index": "APM 设置 - 索引",
"xpack.apmSourcesAccess.apmSettings.index": "APM 设置 - 索引",
"xpack.banners.settings.backgroundColor.description": "设置横幅广告的背景色。{subscriptionLink}",
"xpack.banners.settings.backgroundColor.title": "横幅广告背景色",
"xpack.banners.settings.placement.description": "在 Elastic 页眉上显示顶部横幅广告。{subscriptionLink}",

View file

@ -0,0 +1,5 @@
# APM Sources access
Exposes services to access APM sources.
Services are registered during plugin [start](./server/plugin.ts) phase and defined in the [services](./server/services/) folder.

View file

@ -0,0 +1,35 @@
/*
* 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 TypeOf, schema } from '@kbn/config-schema';
/**
* Schema for APM indices
*/
export const indicesSchema = schema.object({
transaction: schema.string({ defaultValue: 'traces-apm*,apm-*,traces-*.otel-*' }), // TODO: remove apm-* pattern in 9.0
span: schema.string({ defaultValue: 'traces-apm*,apm-*,traces-*.otel-*' }),
error: schema.string({ defaultValue: 'logs-apm*,apm-*,logs-*.otel-*' }),
metric: schema.string({ defaultValue: 'metrics-apm*,apm-*,metrics-*.otel-*' }),
onboarding: schema.string({ defaultValue: 'apm-*' }), // Unused: to be deleted
sourcemap: schema.string({ defaultValue: 'apm-*' }), // Unused: to be deleted
});
/**
* Schema for APM Sources configuration
*/
export const configSchema = schema.object({
indices: indicesSchema,
});
/**
* Schema for APM Sources configuration
*/
export type APMSourcesAccessConfig = TypeOf<typeof configSchema>;
/**
* Schema for APM indices
*/
export type APMIndices = APMSourcesAccessConfig['indices'];

View file

@ -0,0 +1,14 @@
/*
* 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.
*/
const path = require('path');
module.exports = {
preset: '@kbn/test',
rootDir: path.resolve(__dirname, '../../../../..'),
roots: ['<rootDir>/x-pack/platform/plugins/shared/apm_sources_access'],
};

View file

@ -0,0 +1,24 @@
{
"type": "plugin",
"id": "@kbn/apm-sources-access-plugin",
"owner": [
"@elastic/obs-ux-infra_services-team"
],
"group": "platform",
"visibility": "shared",
"plugin": {
"id": "apmSourcesAccess",
"server": true,
"browser": true,
"configPath": [
"xpack",
"apm_sources_access"
],
"requiredPlugins": [
"data",
"dataViews",
],
"optionalPlugins": [],
"requiredBundles": []
}
}

View file

@ -0,0 +1,27 @@
/*
* 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 { HttpStart } from '@kbn/core/public';
import type { APIEndpoint, APIReturnType } from '../server';
export interface SourcesApiOptions {
body?: unknown;
signal?: AbortSignal;
}
export const callSourcesAPI = <T extends APIEndpoint>(
http: HttpStart,
pathname: T,
options?: SourcesApiOptions
): Promise<APIReturnType<T>> => {
const [method, path] = pathname.split(' ');
return http[method.toLowerCase() as 'get' | 'post' | 'put' | 'delete' | 'patch'](path, {
body: options?.body != null ? JSON.stringify(options.body) : undefined,
signal: options?.signal,
});
};

View file

@ -0,0 +1,24 @@
/*
* 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 { PluginInitializer } from '@kbn/core/public';
import {
ApmSourceAccessPlugin,
type ApmSourceAccessPluginSetup,
type ApmSourceAccessPluginStart,
} from './plugin';
export const plugin: PluginInitializer<
ApmSourceAccessPluginSetup,
ApmSourceAccessPluginStart
> = () => new ApmSourceAccessPlugin();
export type { ApmSourceAccessPluginStart, ApmSourceAccessPluginSetup };
export type { APMIndices } from '../common/config_schema';
export { callSourcesAPI, type SourcesApiOptions } from './api';

View file

@ -0,0 +1,30 @@
/*
* 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 { CoreStart } from '@kbn/core/public';
import type { Plugin } from '@kbn/core/public';
import { registerServices } from './register_services';
/**
* APM Source setup services
*/
export type ApmSourceAccessPluginSetup = ReturnType<ApmSourceAccessPlugin['setup']>;
/**
* APM Source start services
*/
export type ApmSourceAccessPluginStart = ReturnType<ApmSourceAccessPlugin['start']>;
export class ApmSourceAccessPlugin
implements Plugin<ApmSourceAccessPluginSetup, ApmSourceAccessPluginStart, {}>
{
public setup() {}
public start(core: CoreStart) {
return registerServices(core);
}
public stop() {}
}

View file

@ -0,0 +1,38 @@
/*
* 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 { HttpStart } from '@kbn/core/public';
import { type SourcesApiOptions, callSourcesAPI } from './api';
import type { APMIndices } from '../common/config_schema';
export interface RegisterServicesParams {
http: HttpStart;
}
export function registerServices(params: RegisterServicesParams) {
return createApmSourcesAccessService(params);
}
export function createApmSourcesAccessService({ http }: RegisterServicesParams) {
const getApmIndexSettings = (options?: Omit<SourcesApiOptions, 'body'>) =>
callSourcesAPI(http, 'GET /internal/apm-sources/settings/apm-index-settings', options);
const getApmIndices = (options?: Omit<SourcesApiOptions, 'body'>) =>
callSourcesAPI(http, 'GET /internal/apm-sources/settings/apm-indices', options);
const saveApmIndices = (
options: SourcesApiOptions & { body: Partial<Record<keyof APMIndices, string>> }
) => callSourcesAPI(http, 'POST /internal/apm-sources/settings/apm-indices/save', options);
const apmSourcesService = {
getApmIndices,
getApmIndexSettings,
saveApmIndices,
};
return apmSourcesService;
}

View file

@ -0,0 +1,112 @@
/*
* 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 { PluginConfigDescriptor } from '@kbn/core/server';
import { type APMSourcesAccessConfig, configSchema } from '../common/config_schema';
/**
* Plugin configuration for the apm_sources_access.
*/
export const config: PluginConfigDescriptor<APMSourcesAccessConfig> = {
deprecations: ({ renameFromRoot, deprecate }) => [
// deprecations
deprecate('indices.sourcemap', 'a future version', {
level: 'warning',
message: `Configuring "xpack.apm.indices.sourcemap" is deprecated and will be removed in a future version. Please remove this setting.`,
}),
deprecate('indices.onboarding', 'a future version', {
level: 'warning',
message: `Configuring "xpack.apm.indices.onboarding" is deprecated and will be removed in a future version. Please remove this setting.`,
}),
// deprecations due to removal of apm_oss plugin
renameFromRoot('apm_oss.transactionIndices', 'xpack.apm.indices.transaction', {
level: 'warning',
}),
renameFromRoot('apm_oss.spanIndices', 'xpack.apm.indices.span', {
level: 'warning',
}),
renameFromRoot('apm_oss.errorIndices', 'xpack.apm.indices.error', {
level: 'warning',
}),
renameFromRoot('apm_oss.metricsIndices', 'xpack.apm.indices.metric', {
level: 'warning',
}),
renameFromRoot('apm_oss.onboardingIndices', 'xpack.apm.indices.onboarding', {
level: 'warning',
}),
// rename from apm to apm_sources_access plugin
renameFromRoot(
'xpack.apm.indices.transaction',
'xpack.apm_sources_access.indices.transaction',
{
level: 'warning',
silent: true,
}
),
renameFromRoot('xpack.apm.indices.span', 'xpack.apm_sources_access.indices.span', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.error', 'xpack.apm_sources_access.indices.error', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.metric', 'xpack.apm_sources_access.indices.metric', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.sourcemap', 'xpack.apm_sources_access.indices.sourcemap', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.onboarding', 'xpack.apm_sources_access.indices.onboarding', {
level: 'warning',
}),
// rename from apm_data_access to apm_sources_access plugin
renameFromRoot(
'xpack.apm_data_access.indices.transaction',
'xpack.apm_sources_access.indices.transaction',
{
level: 'warning',
silent: true,
}
),
renameFromRoot('xpack.apm_data_access.indices.span', 'xpack.apm_sources_access.indices.span', {
level: 'warning',
}),
renameFromRoot(
'xpack.apm_data_access.indices.error',
'xpack.apm_sources_access.indices.error',
{
level: 'warning',
}
),
renameFromRoot(
'xpack.apm_data_access.indices.metric',
'xpack.apm_sources_access.indices.metric',
{
level: 'warning',
}
),
renameFromRoot(
'xpack.apm_data_access.indices.sourcemap',
'xpack.apm_sources_access.indices.sourcemap',
{
level: 'warning',
}
),
renameFromRoot(
'xpack.apm_data_access.indices.onboarding',
'xpack.apm_sources_access.indices.onboarding',
{
level: 'warning',
}
),
],
schema: configSchema,
};

View file

@ -0,0 +1,34 @@
/*
* 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 { PluginInitializerContext } from '@kbn/core/server';
import type { ApmSourcesAccessPluginSetup, ApmSourcesAccessPluginStart } from './plugin';
import { config } from './config';
import {
configSchema,
type APMSourcesAccessConfig,
type APMIndices,
} from '../common/config_schema';
const plugin = async (initContext: PluginInitializerContext) => {
const { ApmSourcesAccessPlugin } = await import('./plugin');
return new ApmSourcesAccessPlugin(initContext);
};
export type {
APIEndpoint,
APIReturnType,
APMSourcesServerRouteRepository,
APIClientRequestParamsOf,
} from './routes';
export type {
APMIndices,
APMSourcesAccessConfig,
ApmSourcesAccessPluginSetup,
ApmSourcesAccessPluginStart,
};
export { configSchema, config, plugin };

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 type {
PluginInitializerContext,
CoreSetup,
Plugin,
SavedObjectsClientContract,
Logger,
} from '@kbn/core/server';
import { registerRoutes } from '@kbn/server-route-repository';
import type { APMSourcesAccessConfig } from '../common/config_schema';
import {
apmIndicesSavedObjectDefinition,
getApmIndicesSavedObject,
} from './saved_objects/apm_indices';
import { apmSourcesSettingsRouteRepository } from './routes';
/**
* APM Source setup services
*/
export type ApmSourcesAccessPluginSetup = ReturnType<ApmSourcesAccessPlugin['setup']>;
/**
* APM Source start services
*/
export type ApmSourcesAccessPluginStart = ReturnType<ApmSourcesAccessPlugin['start']>;
export class ApmSourcesAccessPlugin
implements Plugin<ApmSourcesAccessPluginSetup, ApmSourcesAccessPluginStart>
{
public config: APMSourcesAccessConfig;
public logger: Logger;
constructor(initContext: PluginInitializerContext) {
this.config = initContext.config.get<APMSourcesAccessConfig>();
this.logger = initContext.logger.get();
}
getApmIndices = async (savedObjectsClient: SavedObjectsClientContract) => {
const apmIndicesFromSavedObject = await getApmIndicesSavedObject(savedObjectsClient);
return { ...this.config.indices, ...apmIndicesFromSavedObject };
};
/**
* Registers the saved object definition and ui settings
* for APM Sources.
*/
public setup(core: CoreSetup) {
// register saved object
core.savedObjects.registerType(apmIndicesSavedObjectDefinition);
const services = {
apmIndicesFromConfigFile: this.config.indices,
getApmIndices: this.getApmIndices,
};
registerRoutes({
core,
logger: this.logger,
repository: apmSourcesSettingsRouteRepository,
runDevModeChecks: false,
dependencies: { sources: services },
});
// expose
return services;
}
/**
* Initialises the user value for APM Sources UI settings.
*/
public start() {
return {
getApmIndices: this.getApmIndices,
};
}
public stop() {}
}

View file

@ -0,0 +1,85 @@
/*
* 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 * as t from 'io-ts';
import {
type ReturnOf,
createServerRouteFactory,
type EndpointOf,
type ClientRequestParamsOf,
} from '@kbn/server-route-repository';
import type { SavedObject } from '@kbn/core/server';
import {
type APMSourcesRouteHandlerResources,
getApmIndices,
getApmIndexSettings,
} from './settings';
import { saveApmIndices } from '../saved_objects/apm_indices';
const createApmSourcesServerRoute = createServerRouteFactory<APMSourcesRouteHandlerResources, {}>();
export type APMSourcesServerRouteRepository = typeof apmSourcesSettingsRouteRepository;
export type APIReturnType<TEndpoint extends APIEndpoint> = ReturnOf<
APMSourcesServerRouteRepository,
TEndpoint
>;
export type APIEndpoint = EndpointOf<APMSourcesServerRouteRepository>;
export type APIClientRequestParamsOf<TEndpoint extends APIEndpoint> = ClientRequestParamsOf<
APMSourcesServerRouteRepository,
TEndpoint
>;
// get apm indices configuration object
const apmIndicesRoute = createApmSourcesServerRoute({
endpoint: 'GET /internal/apm-sources/settings/apm-indices',
security: { authz: { requiredPrivileges: ['apm'] } },
handler: getApmIndices,
});
const apmIndexSettingsRoute = createApmSourcesServerRoute({
endpoint: 'GET /internal/apm-sources/settings/apm-index-settings',
security: { authz: { requiredPrivileges: ['apm'] } },
handler: getApmIndexSettings,
});
const saveApmIndicesRoute = createApmSourcesServerRoute({
endpoint: 'POST /internal/apm-sources/settings/apm-indices/save',
security: {
authz: {
requiredPrivileges: ['apm', 'apm_settings_write'],
},
},
params: t.type({
body: t.partial({
error: t.string,
onboarding: t.string,
span: t.string,
transaction: t.string,
metric: t.string,
// Keeping this one here for backward compatibility
sourcemap: t.string,
}),
}),
handler: async ({ params, context }): Promise<SavedObject<{}>> => {
const {
body: { sourcemap, ...indices },
} = params;
const coreContext = await context.core;
return await saveApmIndices(coreContext.savedObjects.client, indices);
},
});
export const apmSourcesSettingsRouteRepository = {
...apmIndicesRoute,
...apmIndexSettingsRoute,
...saveApmIndicesRoute,
};

View file

@ -0,0 +1,59 @@
/*
* 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 { CoreSetup } from '@kbn/core/server';
import type { DefaultRouteHandlerResources } from '@kbn/server-route-repository';
import { getApmIndicesSavedObject } from '../saved_objects/apm_indices';
import type { ApmSourcesAccessPlugin } from '../plugin';
import type { APMIndices } from '..';
export interface APMSourcesRouteHandlerResources extends DefaultRouteHandlerResources {
sources: ReturnType<ApmSourcesAccessPlugin['setup']>;
}
export interface APMSourcesCore {
setup: CoreSetup;
}
export interface ApmIndexSettingsResponse {
apmIndexSettings: Array<{
configurationName: keyof APMIndices;
defaultValue: string; // value defined in kibana[.dev].yml
savedValue: string | undefined;
}>;
}
export async function getApmIndices({
sources,
context,
}: APMSourcesRouteHandlerResources): Promise<APMIndices> {
const coreContext = await context.core;
return await sources.getApmIndices(coreContext.savedObjects.client);
}
export async function getApmIndexSettings({
sources,
context,
}: APMSourcesRouteHandlerResources): Promise<ApmIndexSettingsResponse> {
const { apmIndicesFromConfigFile } = sources;
const coreContext = await context.core;
const apmIndicesSavedObject = await getApmIndicesSavedObject(coreContext.savedObjects.client);
const apmIndicesConfigFile = Object.entries(apmIndicesFromConfigFile) as Array<
[keyof typeof apmIndicesFromConfigFile, string]
>;
return {
apmIndexSettings: apmIndicesConfigFile.map(([configurationName, defaultValue]) => ({
configurationName,
defaultValue, // value defined in kibana[.dev].yml
savedValue: apmIndicesSavedObject?.[configurationName], // value saved via Saved Objects service
})),
};
}

View file

@ -11,7 +11,7 @@ import { schema } from '@kbn/config-schema';
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
import type { SavedObjectsClientContract } from '@kbn/core/server';
import { updateApmOssIndexPaths } from './migrations/update_apm_oss_index_paths';
import type { APMIndices } from '..';
import type { APMIndices } from '../../common/config_schema';
export const APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE = 'apm-indices';
export const APM_INDEX_SETTINGS_SAVED_OBJECT_ID = 'apm-indices';
@ -40,7 +40,7 @@ export const apmIndicesSavedObjectDefinition: SavedObjectsType = {
importableAndExportable: true,
icon: 'apmApp',
getTitle: () =>
i18n.translate('xpack.apmDataAccess.apmSettings.index', {
i18n.translate('xpack.apmSourcesAccess.apmSettings.index', {
defaultMessage: 'APM Settings - Index',
}),
},
@ -88,13 +88,15 @@ export function saveApmIndices(
// remove empty/undefined values
function removeEmpty(apmIndices: Partial<APMIndices>) {
return Object.entries(apmIndices)
.map(([key, value]) => [key, value?.trim()])
.filter(([_, value]) => !!value)
.reduce((obj, [key, value]) => {
return Object.entries<string | undefined>(apmIndices).reduce((obj, [key, value]) => {
value = value?.trim();
if (value) {
obj[key] = value;
return obj;
}, {} as Record<string, unknown>);
}
return obj;
}, {} as Record<string, unknown>);
}
export async function getApmIndicesSavedObject(savedObjectsClient: SavedObjectsClientContract) {

View file

@ -0,0 +1,14 @@
{
"extends": "../../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": ["../../../../typings/**/*", "public/**/*", "server/**/*", "common/**/*"],
"exclude": ["target/**/*"],
"kbn_references": [
"@kbn/config-schema",
"@kbn/core",
"@kbn/i18n",
"@kbn/server-route-repository",
]
}

View file

@ -48,7 +48,7 @@ describe('No data screen', () => {
function setApmIndices(body: Record<string, string>) {
cy.request({
url: '/internal/apm/settings/apm-indices/save',
url: '/internal/apm-sources/settings/apm-indices/save',
method: 'POST',
body,
headers: { 'kbn-xsrf': true },

View file

@ -14,7 +14,9 @@ const getAbleToModifyCase = () => {
const input = cy.get('input[name="error"]');
input.should('not.be.disabled');
input.clear().type(newErrorIndex);
cy.intercept('POST', '/internal/apm/settings/apm-indices/save*').as('internalApiRequest');
cy.intercept('POST', '/internal/apm-sources/settings/apm-indices/save*').as(
'internalApiRequest'
);
cy.contains('Apply changes').should('not.be.disabled').click();
cy.wait('@internalApiRequest').its('response.statusCode').should('eq', 200);
});

View file

@ -16,6 +16,7 @@
"apm"
],
"requiredPlugins": [
"apmSourcesAccess",
"apmDataAccess",
"data",
"dashboard",
@ -73,4 +74,4 @@
"maps"
]
}
}
}

View file

@ -23,14 +23,12 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React, { useEffect, useState } from 'react';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/public';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useFetcher } from '../../../../hooks/use_fetcher';
import type { ApmPluginStartDeps } from '../../../../plugin';
import { clearCache } from '../../../../services/rest/call_api';
import type { APIReturnType } from '../../../../services/rest/create_call_apm_api';
import { callApmApi } from '../../../../services/rest/create_call_apm_api';
const APM_INDEX_LABELS = [
const APM_INDEX_LABELS: ReadonlyArray<{ configurationName: keyof APMIndices; label: string }> = [
{
configurationName: 'error',
label: i18n.translate('xpack.apm.settings.apmIndices.errorIndicesLabel', {
@ -63,23 +61,8 @@ const APM_INDEX_LABELS = [
},
];
async function saveApmIndices({ apmIndices }: { apmIndices: Record<string, string> }) {
await callApmApi('POST /internal/apm/settings/apm-indices/save', {
signal: null,
params: {
body: apmIndices,
},
});
clearCache();
// Reload window to update APM data view with new indices
window.location.reload();
}
type ApiResponse = APIReturnType<`GET /internal/apm/settings/apm-index-settings`>;
// avoid infinite loop by initializing the state outside the component
const INITIAL_STATE: ApiResponse = { apmIndexSettings: [] };
const INITIAL_STATE = { apmIndexSettings: [] };
export function ApmIndices() {
const { core } = useApmPluginContext();
@ -91,12 +74,13 @@ export function ApmIndices() {
application.capabilities.apm['settings:save'] &&
application.capabilities.savedObjectsManagement.edit;
const [apmIndices, setApmIndices] = useState<Record<string, string>>({});
const [apmIndices, setApmIndices] = useState<Partial<Record<keyof APMIndices, string>>>({});
const [isSaving, setIsSaving] = useState(false);
const { data = INITIAL_STATE, refetch } = useFetcher((_callApmApi) => {
return _callApmApi(`GET /internal/apm/settings/apm-index-settings`);
}, []);
const { data = INITIAL_STATE, refetch } = useFetcher(
(_, signal) => services.apmSourcesAccess.getApmIndexSettings({ signal }),
[services.apmSourcesAccess]
);
const { data: space } = useFetcher(() => {
return services.spaces?.getActiveSpace();
@ -109,7 +93,7 @@ export function ApmIndices() {
...acc,
[configurationName]: savedValue,
}),
{}
{} as APMIndices
)
);
}, [data]);
@ -120,7 +104,7 @@ export function ApmIndices() {
event.preventDefault();
setIsSaving(true);
try {
await saveApmIndices({ apmIndices });
await services.apmSourcesAccess.saveApmIndices({ body: apmIndices });
notifications.toasts.addSuccess({
title: i18n.translate('xpack.apm.settings.apmIndices.applyChanges.succeeded.title', {
defaultMessage: 'Indices applied',
@ -130,6 +114,8 @@ export function ApmIndices() {
'The indices changes were successfully applied. These changes are reflected immediately in the APM UI',
}),
});
// Defer reload once the UI has finished rendering
setTimeout(() => window.location.reload());
} catch (error: any) {
notifications.toasts.addDanger({
title: i18n.translate('xpack.apm.settings.apmIndices.applyChanges.failed.title', {
@ -140,8 +126,9 @@ export function ApmIndices() {
values: { errorMessage: error.message },
}),
});
} finally {
setIsSaving(false);
}
setIsSaving(false);
};
const handleChangeIndexConfigurationEvent = async (
@ -205,7 +192,7 @@ export function ApmIndices() {
({ configurationName: configName }) => configName === configurationName
);
const defaultValue = matchedConfiguration ? matchedConfiguration.defaultValue : '';
const savedUiIndexValue = apmIndices[configurationName] || '';
const savedUiIndexValue = apmIndices[configurationName];
return (
<EuiFormRow
key={configurationName}

View file

@ -82,7 +82,7 @@ type InferResponseType<TReturn> = Exclude<TReturn, undefined> extends Promise<in
: unknown;
export function useFetcher<TReturn>(
fn: (callApmApi: AutoAbortedAPMClient) => TReturn,
fn: (callApmApi: AutoAbortedAPMClient, signal: AbortSignal) => TReturn,
fnDeps: any[],
options: {
preservePreviousData?: boolean;
@ -125,7 +125,7 @@ export function useFetcher<TReturn>(
const signal = controller.signal;
const promise = fn(createAutoAbortedAPMClient(signal, addInspectorRequest));
const promise = fn(createAutoAbortedAPMClient(signal, addInspectorRequest), signal);
// if `fn` doesn't return a promise it is a signal that data fetching was not initiated.
// This can happen if the data fetching is conditional (based on certain inputs).
// In these cases it is not desirable to invoke the global loading spinner, or change the status to success

View file

@ -74,6 +74,7 @@ import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/pub
import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public';
import type { SharePublicStart } from '@kbn/share-plugin/public/plugin';
import type { ApmSourceAccessPluginStart } from '@kbn/apm-sources-access-plugin/public';
import type { ConfigSchema } from '.';
import { registerApmRuleTypes } from './components/alerting/rule_types/register_apm_rule_types';
import { registerEmbeddables } from './embeddable/register_embeddables';
@ -90,7 +91,7 @@ import type { ITelemetryClient } from './services/telemetry';
import { TelemetryService } from './services/telemetry';
export type ApmPluginSetup = ReturnType<ApmPlugin['setup']>;
export type ApmPluginStart = void;
export type ApmPluginStart = ReturnType<ApmPlugin['start']>;
export interface ApmPluginSetupDeps {
alerting?: AlertingPluginPublicSetup;
@ -149,6 +150,7 @@ export interface ApmPluginStartDeps {
uiSettings: IUiSettingsClient;
logsShared: LogsSharedClientStartExports;
logsDataAccess: LogsDataAccessPluginStart;
apmSourcesAccess: ApmSourceAccessPluginStart;
savedSearch: SavedSearchPublicPluginStart;
fieldsMetadata: FieldsMetadataPublicStart;
share?: SharePublicStart;

View file

@ -11,7 +11,7 @@ import { Client, HttpConnection } from '@elastic/elasticsearch';
import fs from 'fs/promises';
import type { AxiosRequestConfig } from 'axios';
import axios from 'axios';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { APIReturnType } from '../../public/services/rest/create_call_apm_api';
import { getDiagnosticsBundle } from '../../server/routes/diagnostics/get_diagnostics_bundle';

View file

@ -13,7 +13,7 @@ import type {
LicensingApiRequestHandlerContext,
} from '@kbn/licensing-plugin/server';
import { APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE } from '@kbn/apm-data-access-plugin/server/saved_objects/apm_indices';
import { APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE } from '@kbn/apm-sources-access-plugin/server/saved_objects/apm_indices';
import { ApmRuleType } from '@kbn/rule-data-utils';
import { ALERTING_FEATURE_ID } from '@kbn/alerting-plugin/common';
import type { KibanaFeatureConfig } from '@kbn/features-plugin/common';

View file

@ -11,7 +11,7 @@ import { snakeCase } from 'lodash';
import moment from 'moment';
import { v4 as uuidv4 } from 'uuid';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { ElasticsearchCapabilities } from '@kbn/core-elasticsearch-server';
import { ML_ERRORS } from '../../../common/anomaly_detection';
import { METRICSET_NAME, PROCESSOR_EVENT } from '../../../common/es_fields/apm';

View file

@ -6,7 +6,7 @@
*/
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { tasks } from './tasks';
import { SERVICE_NAME, SERVICE_ENVIRONMENT, AT_TIMESTAMP } from '../../../../common/es_fields/apm';
import type { IndicesStatsResponse } from '../telemetry_client';

View file

@ -10,7 +10,7 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { createHash } from 'crypto';
import { flatten, merge, pickBy, sortBy, sum, uniq, without } from 'lodash';
import type { SavedObjectsClient } from '@kbn/core/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
AGENT_NAMES,
OPEN_TELEMETRY_AGENT_NAMES,

View file

@ -12,7 +12,7 @@ import type {
TaskManagerSetupContract,
TaskManagerStartContract,
} from '@kbn/task-manager-plugin/server';
import type { APMDataAccessConfig } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
APM_TELEMETRY_SAVED_OBJECT_ID,
APM_TELEMETRY_SAVED_OBJECT_TYPE,
@ -35,7 +35,7 @@ export async function createApmTelemetry({
isProd,
}: {
core: CoreSetup;
getApmIndices: (soClient: SavedObjectsClientContract) => Promise<APMDataAccessConfig['indices']>;
getApmIndices: (soClient: SavedObjectsClientContract) => Promise<APMIndices>;
usageCollector: UsageCollectionSetup;
taskManager: TaskManagerSetupContract;
logger: Logger;

View file

@ -14,7 +14,7 @@ import type { IRuleDataClient } from '@kbn/rule-registry-plugin/server';
import type { MlPluginSetup } from '@kbn/ml-plugin/server';
import type { ObservabilityApmAlert } from '@kbn/alerts-as-data-utils';
import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
AGENT_NAME,
CONTAINER_ID,

View file

@ -10,7 +10,7 @@ import type {
IUiSettingsClient,
SavedObjectsClientContract,
} from '@kbn/core/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,

View file

@ -32,7 +32,7 @@ import type { InspectResponse } from '@kbn/observability-plugin/typings/common';
import apm from 'elastic-apm-node';
import type { VersionedRouteRegistrar } from '@kbn/core-http-server';
import type { IRuleDataClient } from '@kbn/rule-registry-plugin/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { ApmFeatureFlags } from '../../../common/apm_feature_flags';
import type {
APMCore,

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { Logger } from '@kbn/core/server';
import type { DataViewsService } from '@kbn/data-views-plugin/common';
import type { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { getApmDataViewIndexPattern } from './get_apm_data_view_index_pattern';
describe('getApmDataViewIndexPattern', () => {

View file

@ -6,7 +6,7 @@
*/
import { uniq } from 'lodash';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
export function getApmDataViewIndexPattern(apmIndices: APMIndices) {
return uniq([apmIndices.transaction, apmIndices.span, apmIndices.error, apmIndices.metric]).join(

View file

@ -8,7 +8,7 @@
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import { kqlQuery, rangeQuery } from '@kbn/observability-plugin/server';
import { merge } from 'lodash';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
PROCESSOR_EVENT,
METRICSET_NAME,

View file

@ -6,7 +6,7 @@
*/
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { getApmIndexPatterns } from './get_indices';
export async function getDataStreams({

View file

@ -6,7 +6,7 @@
*/
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { SERVICE_NAME } from '../../../../common/es_fields/apm';
import { getApmIndexPatterns } from './get_indices';

View file

@ -9,7 +9,7 @@ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { IndicesSimulateTemplateResponse } from '@elastic/elasticsearch/lib/api/types';
import { orderBy } from 'lodash';
import { errors } from '@elastic/elasticsearch';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { getApmIndexPatterns } from './get_indices';
import { getIndexTemplate } from './get_index_template';
import { getApmIndexTemplateNames } from '../helpers/get_apm_index_template_names';

View file

@ -7,7 +7,7 @@
import { compact, uniq } from 'lodash';
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
export function getApmIndexPatterns(indices: string[]) {
return uniq(indices.flatMap((index): string[] => index.split(',')));

View file

@ -6,7 +6,7 @@
*/
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { SERVICE_NAME } from '../../../../common/es_fields/apm';
import { getApmIndexTemplateNames } from '../helpers/get_apm_index_template_names';
import { getFieldCaps } from './get_field_caps';

View file

@ -6,7 +6,7 @@
*/
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { getApmIndexPatterns } from './get_indices';
export async function getNonDataStreamIndices({

View file

@ -6,7 +6,7 @@
*/
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { NOT_AVAILABLE_LABEL } from '../../../common/i18n';
import { getDataStreams } from './bundle/get_data_streams';
import { getNonDataStreamIndices } from './bundle/get_non_data_stream_indices';

View file

@ -6,7 +6,7 @@
*/
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { getApmIndexPatterns } from '../bundle/get_indices';
export async function getDiagnosticsPrivileges({

View file

@ -13,7 +13,7 @@ import type {
IngestGetPipelineResponse,
SecurityHasPrivilegesPrivileges,
} from '@elastic/elasticsearch/lib/api/types';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import * as t from 'io-ts';
import { isoToEpochRt } from '@kbn/io-ts-utils';
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';

View file

@ -12,7 +12,7 @@ import type {
KibanaRequest,
} from '@kbn/core/server';
import type { PackagePolicy } from '@kbn/fleet-plugin/common';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
APM_SERVER_SCHEMA_SAVED_OBJECT_TYPE,
APM_SERVER_SCHEMA_SAVED_OBJECT_ID,

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { NewPackagePolicy } from '@kbn/fleet-plugin/common';
import type { APMInternalESClient } from '../../lib/helpers/create_es_client/create_internal_es_client';
import type { APMPluginStartDependencies } from '../../types';

View file

@ -14,7 +14,7 @@ import type {
PutPackagePolicyUpdateCallback,
} from '@kbn/fleet-plugin/server';
import { get } from 'lodash';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { decoratePackagePolicyWithAgentConfigAndSourceMap } from './merge_package_policy_with_apm';
import { addApiKeysToPackagePolicyIfMissing } from './api_keys/add_api_keys_to_policies_if_missing';
import {

View file

@ -6,7 +6,7 @@
*/
import type { CoreStart, SavedObjectsClientContract } from '@kbn/core/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { TelemetryUsageCounter } from '../typings';
import type { APMPluginStartDependencies } from '../../types';
import { getInternalSavedObjectsClient } from '../../lib/helpers/get_internal_saved_objects_client';

View file

@ -6,7 +6,7 @@
*/
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
export interface HasDataResponse {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client';
import type { AgentConfiguration } from '../../../../common/agent_configuration/configuration_types';
import { convertConfigSettingsToString } from './convert_settings_to_string';

View file

@ -9,7 +9,7 @@ import { uniq } from 'lodash';
import pLimit from 'p-limit';
import type { ElasticsearchClient } from '@kbn/core/server';
import { JOB_STATE } from '@kbn/ml-plugin/common';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { ElasticsearchCapabilities } from '@kbn/core-elasticsearch-server';
import { createAnomalyDetectionJobs } from '../../../lib/anomaly_detection/create_anomaly_detection_jobs';
import { getAnomalyDetectionJobs } from '../../../lib/anomaly_detection/get_anomaly_detection_jobs';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getApmIndicesSavedObject } from '@kbn/apm-data-access-plugin/server/saved_objects/apm_indices';
import { getApmIndicesSavedObject } from '@kbn/apm-sources-access-plugin/server/saved_objects/apm_indices';
import type { APMRouteHandlerResources } from '../../apm_routes/register_apm_server_routes';
export type ApmIndexSettingsResponse = Array<{

View file

@ -7,12 +7,13 @@
import * as t from 'io-ts';
import type { SavedObject } from '@kbn/core/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import { saveApmIndices } from '@kbn/apm-data-access-plugin/server/saved_objects/apm_indices';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { saveApmIndices } from '@kbn/apm-sources-access-plugin/server/saved_objects/apm_indices';
import { createApmServerRoute } from '../../apm_routes/create_apm_server_route';
import type { ApmIndexSettingsResponse } from './get_apm_indices';
import { getApmIndexSettings } from './get_apm_indices';
// TODO: Deprecate and remove these API routes. https://github.com/elastic/kibana/issues/214570
// get list of apm indices and values
const apmIndexSettingsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/settings/apm-index-settings',
@ -27,6 +28,7 @@ const apmIndexSettingsRoute = createApmServerRoute({
},
});
// TODO: Deprecate and remove these API routes. https://github.com/elastic/kibana/issues/214570
// get apm indices configuration object
const apmIndicesRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/settings/apm-indices',
@ -40,6 +42,7 @@ type SaveApmIndicesBodySchema = {
[Property in keyof APMIndices]: t.StringC;
};
// TODO: Deprecate and remove these API routes. https://github.com/elastic/kibana/issues/214570
// save ui indices
const saveApmIndicesRoute = createApmServerRoute({
endpoint: 'POST /internal/apm/settings/apm-indices/save',

View file

@ -6,7 +6,7 @@
*/
import { isCrossClusterSearch } from './is_cross_cluster_search';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
describe('isCrossClusterSearch', () => {

View file

@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
import type { TutorialSchema, InstructionSetSchema } from '@kbn/home-plugin/server';
import { INSTRUCTION_VARIANT } from '@kbn/home-plugin/server';
import type { CloudSetup } from '@kbn/cloud-plugin/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
createNodeAgentInstructions,
createDjangoAgentInstructions,

View file

@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';
import type { InstructionsSchema } from '@kbn/home-plugin/server';
import { INSTRUCTION_VARIANT } from '@kbn/home-plugin/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
createDjangoAgentInstructions,
createDotNetAgentInstructions,

View file

@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';
import type { InstructionsSchema } from '@kbn/home-plugin/server';
import { INSTRUCTION_VARIANT } from '@kbn/home-plugin/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import {
createDownloadServerDeb,
createDownloadServerOsx,

View file

@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
import type { ArtifactsSchema, TutorialSchema } from '@kbn/home-plugin/server';
import { TutorialsCategory } from '@kbn/home-plugin/server';
import type { CloudSetup } from '@kbn/cloud-plugin/server';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { APMConfig } from '..';
import { createElasticCloudInstructions } from './envs/elastic_cloud';
import { onPremInstructions } from './envs/on_prem';

View file

@ -6,7 +6,7 @@
*/
import type { ESSearchRequest, ESSearchResponse } from '@kbn/es-types';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { APMConfig } from '..';
import type { APMEventClient } from '../lib/helpers/create_es_client/create_apm_event_client';
import type { APMInternalESClient } from '../lib/helpers/create_es_client/create_internal_es_client';

View file

@ -137,7 +137,8 @@
"@kbn/event-stacktrace",
"@kbn/response-ops-rule-form",
"@kbn/fields-metadata-plugin",
"@kbn/deeplinks-analytics"
"@kbn/deeplinks-analytics",
"@kbn/apm-sources-access-plugin",
],
"exclude": ["target/**/*"]
}

View file

@ -13,11 +13,12 @@
"apm_data_access"
],
"requiredPlugins": [
"data"
"data",
"apmSourcesAccess"
],
"optionalPlugins": [
"security"
],
"requiredBundles": []
}
}
}

View file

@ -5,78 +5,13 @@
* 2.0.
*/
import type { TypeOf } from '@kbn/config-schema';
import { schema } from '@kbn/config-schema';
import type { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server';
const configSchema = schema.object({
indices: schema.object({
transaction: schema.string({ defaultValue: 'traces-apm*,apm-*,traces-*.otel-*' }), // TODO: remove apm-* pattern in 9.0
span: schema.string({ defaultValue: 'traces-apm*,apm-*,traces-*.otel-*' }),
error: schema.string({ defaultValue: 'logs-apm*,apm-*,logs-*.otel-*' }),
metric: schema.string({ defaultValue: 'metrics-apm*,apm-*,metrics-*.otel-*' }),
onboarding: schema.string({ defaultValue: 'apm-*' }), // Unused: to be deleted
sourcemap: schema.string({ defaultValue: 'apm-*' }), // Unused: to be deleted
}),
});
// plugin config
export const config: PluginConfigDescriptor<APMDataAccessConfig> = {
deprecations: ({ renameFromRoot, unused, deprecate }) => [
// deprecations
deprecate('indices.sourcemap', 'a future version', {
level: 'warning',
message: `Configuring "xpack.apm.indices.sourcemap" is deprecated and will be removed in a future version. Please remove this setting.`,
}),
deprecate('indices.onboarding', 'a future version', {
level: 'warning',
message: `Configuring "xpack.apm.indices.onboarding" is deprecated and will be removed in a future version. Please remove this setting.`,
}),
// deprecations due to removal of apm_oss plugin
renameFromRoot('apm_oss.transactionIndices', 'xpack.apm.indices.transaction', {
level: 'warning',
}),
renameFromRoot('apm_oss.spanIndices', 'xpack.apm.indices.span', {
level: 'warning',
}),
renameFromRoot('apm_oss.errorIndices', 'xpack.apm.indices.error', {
level: 'warning',
}),
renameFromRoot('apm_oss.metricsIndices', 'xpack.apm.indices.metric', {
level: 'warning',
}),
renameFromRoot('apm_oss.onboardingIndices', 'xpack.apm.indices.onboarding', {
level: 'warning',
}),
// rename from apm to apm_data_access plugin
renameFromRoot('xpack.apm.indices.transaction', 'xpack.apm_data_access.indices.transaction', {
level: 'warning',
silent: true,
}),
renameFromRoot('xpack.apm.indices.span', 'xpack.apm_data_access.indices.span', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.error', 'xpack.apm_data_access.indices.error', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.metric', 'xpack.apm_data_access.indices.metric', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.sourcemap', 'xpack.apm_data_access.indices.sourcemap', {
level: 'warning',
}),
renameFromRoot('xpack.apm.indices.onboarding', 'xpack.apm_data_access.indices.onboarding', {
level: 'warning',
}),
],
schema: configSchema,
export const config: PluginConfigDescriptor = {
schema: schema.never(),
};
export type APMDataAccessConfig = TypeOf<typeof configSchema>;
export type APMIndices = APMDataAccessConfig['indices'];
export async function plugin(initializerContext: PluginInitializerContext) {
const { ApmDataAccessPlugin } = await import('./plugin');

View file

@ -7,7 +7,8 @@
import type { KibanaRequest } from '@kbn/core-http-server';
import type { SecurityPluginStart } from '@kbn/security-plugin-types-server';
import type { APMIndices } from '..';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
export interface ApmDataAccessPrivilegesCheck {
request: KibanaRequest;
security?: SecurityPluginStart;
@ -16,8 +17,7 @@ export interface ApmDataAccessPrivilegesCheck {
export function convertIndiciesToPrivilege(apmIndices: APMIndices) {
return Object.values(apmIndices)
.map((value) => value.split(','))
.flat()
.flatMap((value) => value.split(','))
.reduce<Record<string, string[]>>((obj, item, index) => {
obj[item] = ['read'];
return obj;

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { APMIndices } from '../../../..';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type { APMEventESSearchRequest } from '.';
import { getRequestBase } from './get_request_base';

View file

@ -9,7 +9,7 @@ import type { ESFilter } from '@kbn/es-types';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { uniq } from 'lodash';
import { PROCESSOR_EVENT } from '@kbn/apm-types/es_fields';
import type { APMIndices } from '../../../..';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { getConfigForDocumentType, getProcessorEventForDocumentType } from '../document_type';
import type { ApmDataSource } from '../../../../../common/data_source';

View file

@ -15,7 +15,7 @@ import type {
} from '@elastic/elasticsearch/lib/api/types';
import supertest from 'supertest';
import { APMEventClient, type APMEventESSearchRequest, type APMEventFieldCapsRequest } from '.';
import type { APMIndices } from '../../../..';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import * as cancelEsRequestOnAbortModule from '../cancel_es_request_on_abort';
import * as observabilityPluginModule from '@kbn/observability-plugin/server';

View file

@ -25,12 +25,12 @@ import type { APMError, Metric, Span, Transaction, Event } from '@kbn/apm-types/
import type { InspectResponse } from '@kbn/observability-plugin/typings/common';
import type { DataTier } from '@kbn/observability-shared-plugin/common';
import { excludeTiersQuery } from '@kbn/observability-utils-common/es/queries/exclude_tiers_query';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import { withApmSpan } from '../../../../utils';
import type { ApmDataSource } from '../../../../../common/data_source';
import { cancelEsRequestOnAbort } from '../cancel_es_request_on_abort';
import { callAsyncWithDebug, getDebugBody, getDebugTitle } from '../call_async_with_debug';
import type { ProcessorEventOfDocumentType } from '../document_type';
import type { APMIndices } from '../../../..';
import { getRequestBase, processorEventsToIndex } from './get_request_base';
import { getDataTierFilterCombined } from '../../tier_filter';

View file

@ -10,21 +10,16 @@ import type {
CoreSetup,
CoreStart,
Plugin,
SavedObjectsClientContract,
Logger,
KibanaRequest,
} from '@kbn/core/server';
import type { APMDataAccessConfig } from '.';
import type { APMIndices } from '@kbn/apm-sources-access-plugin/server';
import type {
ApmDataAccessPluginSetup,
ApmDataAccessPluginStart,
ApmDataAccessServerDependencies,
ApmDataAccessServerSetupDependencies,
} from './types';
import { migrateLegacyAPMIndicesToSpaceAware } from './saved_objects/migrations/migrate_legacy_apm_indices_to_space_aware';
import {
apmIndicesSavedObjectDefinition,
getApmIndicesSavedObject,
} from './saved_objects/apm_indices';
import { getServices } from './services/get_services';
import type { ApmDataAccessPrivilegesCheck } from './lib/check_privileges';
import { checkPrivileges } from './lib/check_privileges';
@ -32,42 +27,33 @@ import { checkPrivileges } from './lib/check_privileges';
export class ApmDataAccessPlugin
implements Plugin<ApmDataAccessPluginSetup, ApmDataAccessPluginStart>
{
public server?: ApmDataAccessServerDependencies;
public config: APMDataAccessConfig;
public config?: APMIndices;
public logger: Logger;
constructor(initContext: PluginInitializerContext) {
this.config = initContext.config.get<APMDataAccessConfig>();
this.logger = initContext.logger.get();
}
getApmIndices = async (savedObjectsClient: SavedObjectsClientContract) => {
const apmIndicesFromSavedObject = await getApmIndicesSavedObject(savedObjectsClient);
return { ...this.config.indices, ...apmIndicesFromSavedObject };
};
public setup(core: CoreSetup): ApmDataAccessPluginSetup {
// register saved object
core.savedObjects.registerType(apmIndicesSavedObjectDefinition);
public setup(
core: CoreSetup,
plugins: ApmDataAccessServerSetupDependencies
): ApmDataAccessPluginSetup {
this.config = plugins.apmSourcesAccess.apmIndicesFromConfigFile;
// expose
return {
apmIndicesFromConfigFile: this.config.indices,
getApmIndices: this.getApmIndices,
// TODO: Deprecate and replace with apmSourcesAccess
apmIndicesFromConfigFile: this.config,
// TODO: Deprecate and replace with apmSourcesAccess
getApmIndices: plugins.apmSourcesAccess.getApmIndices,
getServices,
};
}
public start(core: CoreStart, plugins: ApmDataAccessServerDependencies) {
// TODO: remove in 9.0
migrateLegacyAPMIndicesToSpaceAware({ coreStart: core, logger: this.logger }).catch((e) => {
this.logger.error('Failed to run migration making APM indices space aware');
this.logger.error(e);
});
const getApmIndicesWithInternalUserFn = async (request: KibanaRequest) => {
const getApmIndicesWithInternalUserFn = (request: KibanaRequest) => {
const soClient = core.savedObjects.getScopedClient(request);
return this.getApmIndices(soClient);
return plugins.apmSourcesAccess.getApmIndices(soClient);
};
const startServices = {
@ -79,7 +65,7 @@ export class ApmDataAccessPlugin
}),
};
return { ...startServices };
return startServices;
}
public stop() {}

View file

@ -1,210 +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 type { CoreStart, Logger } from '@kbn/core/server';
import {
APM_INDEX_SETTINGS_SAVED_OBJECT_ID,
APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE,
} from '../apm_indices';
import { migrateLegacyAPMIndicesToSpaceAware } from './migrate_legacy_apm_indices_to_space_aware';
const loggerMock = {
debug: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
} as unknown as Logger;
describe('migrateLegacyAPMIndicesToSpaceAware', () => {
describe('when legacy APM indices is not found', () => {
const mockBulkCreate = jest.fn();
const mockCreate = jest.fn();
const mockFind = jest.fn();
const core = {
savedObjects: {
createInternalRepository: jest.fn().mockReturnValue({
get: () => {
throw new Error('BOOM');
},
find: mockFind,
bulkCreate: mockBulkCreate,
create: mockCreate,
}),
},
} as unknown as CoreStart;
it('does not save any new saved object', async () => {
await migrateLegacyAPMIndicesToSpaceAware({
coreStart: core,
logger: loggerMock,
});
expect(mockFind).not.toHaveBeenCalled();
expect(mockBulkCreate).not.toHaveBeenCalled();
expect(mockCreate).not.toHaveBeenCalled();
});
});
describe('when only default space is available', () => {
const mockBulkCreate = jest.fn();
const mockCreate = jest.fn();
const mockSpaceFind = jest.fn().mockReturnValue({
page: 1,
per_page: 10000,
total: 3,
saved_objects: [
{
type: 'space',
id: 'default',
attributes: {
name: 'Default',
},
references: [],
migrationVersion: {
space: '6.6.0',
},
coreMigrationVersion: '8.2.0',
updated_at: '2022-02-22T14:13:28.839Z',
version: 'WzI4OSwxXQ==',
score: 0,
},
],
});
const core = {
savedObjects: {
createInternalRepository: jest.fn().mockReturnValue({
get: jest.fn().mockReturnValue({
id: 'apm-indices',
type: 'apm-indices',
namespaces: [],
updated_at: '2022-02-22T14:17:10.584Z',
version: 'WzE1OSwxXQ==',
attributes: {
apmIndices: {
transaction: 'default-apm-*',
span: 'default-apm-*',
error: 'default-apm-*',
metric: 'default-apm-*',
sourcemap: 'default-apm-*',
onboarding: 'default-apm-*',
},
},
references: [],
migrationVersion: {
'apm-indices': '7.16.0',
},
coreMigrationVersion: '8.2.0',
}),
find: mockSpaceFind,
bulkCreate: mockBulkCreate,
create: mockCreate,
}),
},
} as unknown as CoreStart;
it('creates new default saved object with space awareness and delete legacy', async () => {
await migrateLegacyAPMIndicesToSpaceAware({
coreStart: core,
logger: loggerMock,
});
expect(mockCreate).toBeCalledWith(
APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE,
{
apmIndices: {
transaction: 'default-apm-*',
span: 'default-apm-*',
error: 'default-apm-*',
metric: 'default-apm-*',
sourcemap: 'default-apm-*',
onboarding: 'default-apm-*',
},
isSpaceAware: true,
},
{
id: APM_INDEX_SETTINGS_SAVED_OBJECT_ID,
overwrite: true,
}
);
});
});
describe('when multiple spaces are found', () => {
const mockBulkCreate = jest.fn();
const mockCreate = jest.fn();
const savedObjects = [
{ id: 'default', name: 'Default' },
{ id: 'space-a', name: 'Space A' },
{ id: 'space-b', name: 'Space B' },
];
const mockSpaceFind = jest.fn().mockReturnValue({
page: 1,
per_page: 10000,
total: 3,
saved_objects: savedObjects.map(({ id, name }) => {
return {
type: 'space',
id,
attributes: { name },
references: [],
migrationVersion: { space: '6.6.0' },
coreMigrationVersion: '8.2.0',
updated_at: '2022-02-22T14:13:28.839Z',
version: 'WzI4OSwxXQ==',
score: 0,
};
}),
});
const attributes = {
apmIndices: {
transaction: 'space-apm-*',
span: 'space-apm-*',
error: 'space-apm-*',
metric: 'space-apm-*',
sourcemap: 'space-apm-*',
onboarding: 'space-apm-*',
},
};
const core = {
savedObjects: {
createInternalRepository: jest.fn().mockReturnValue({
get: jest.fn().mockReturnValue({
id: 'apm-indices',
type: 'apm-indices',
namespaces: [],
updated_at: '2022-02-22T14:17:10.584Z',
version: 'WzE1OSwxXQ==',
attributes,
references: [],
migrationVersion: {
'apm-indices': '7.16.0',
},
coreMigrationVersion: '8.2.0',
}),
find: mockSpaceFind,
bulkCreate: mockBulkCreate,
create: mockCreate,
}),
},
} as unknown as CoreStart;
it('creates multiple saved objects with space awareness and delete legacies', async () => {
await migrateLegacyAPMIndicesToSpaceAware({
coreStart: core,
logger: loggerMock,
});
expect(mockCreate).toBeCalled();
expect(mockBulkCreate).toBeCalledWith(
savedObjects
.filter(({ id }) => id !== 'default')
.map(({ id }) => {
return {
type: APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE,
id: APM_INDEX_SETTINGS_SAVED_OBJECT_ID,
initialNamespaces: [id],
attributes: { ...attributes, isSpaceAware: true },
};
})
);
});
});
});

View file

@ -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 type { CoreStart, Logger, ISavedObjectsRepository } from '@kbn/core/server';
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
import type { APMIndices } from '../..';
import type { APMIndicesSavedObjectBody } from '../apm_indices';
import {
APM_INDEX_SETTINGS_SAVED_OBJECT_ID,
APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE,
} from '../apm_indices';
async function fetchLegacyAPMIndices(repository: ISavedObjectsRepository) {
try {
const apmIndices = await repository.get<Partial<APMIndicesSavedObjectBody>>(
APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE,
APM_INDEX_SETTINGS_SAVED_OBJECT_ID
);
if (apmIndices.attributes.isSpaceAware) {
// This has already been migrated to become space-aware
return null;
}
return apmIndices;
} catch (err) {
if (SavedObjectsErrorHelpers.isNotFoundError(err)) {
// This can happen if APM is not being used
return null;
}
throw err;
}
}
export async function migrateLegacyAPMIndicesToSpaceAware({
coreStart,
logger,
}: {
coreStart: CoreStart;
logger: Logger;
}) {
const repository = coreStart.savedObjects.createInternalRepository(['space']);
try {
// Fetch legacy APM indices
const legacyAPMIndices = await fetchLegacyAPMIndices(repository);
if (legacyAPMIndices === null) {
return;
}
// Fetch spaces available
const spaces = await repository.find({
type: 'space',
page: 1,
perPage: 10_000, // max number of spaces as of 8.2
fields: ['name'], // to avoid fetching *all* fields
});
const savedObjectAttributes = {
...legacyAPMIndices.attributes,
isSpaceAware: true,
};
// Calls create first to update the default space setting isSpaceAware to true
await repository.create<Partial<APMIndices & { isSpaceAware: boolean }>>(
APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE,
savedObjectAttributes,
{
id: APM_INDEX_SETTINGS_SAVED_OBJECT_ID,
overwrite: true,
}
);
// Create new APM indices space aware for all spaces available
await repository.bulkCreate<Partial<APMIndicesSavedObjectBody>>(
spaces.saved_objects
// Skip default space since it was already updated
.filter(({ id: spaceId }) => spaceId !== 'default')
.map(({ id: spaceId }) => ({
id: APM_INDEX_SETTINGS_SAVED_OBJECT_ID,
type: APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE,
initialNamespaces: [spaceId],
attributes: savedObjectAttributes,
}))
);
} catch (e) {
logger.error('Failed to migrate legacy APM indices object: ' + e.message);
}
}

View file

@ -7,7 +7,11 @@
import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import type { SecurityPluginStart } from '@kbn/security-plugin-types-server';
import type { APMIndices } from '.';
import type {
ApmSourcesAccessPluginStart,
ApmSourcesAccessPluginSetup,
APMIndices,
} from '@kbn/apm-sources-access-plugin/server';
import type { getServices } from './services/get_services';
import type { ApmDataAccessPrivilegesCheck } from './lib/check_privileges';
@ -17,16 +21,18 @@ export interface ApmDataAccessPluginSetup {
getServices: typeof getServices;
}
export interface ApmDataAccessServerSetupDependencies {
apmSourcesAccess: ApmSourcesAccessPluginSetup;
}
export interface ApmDataAccessServerDependencies {
apmSourcesAccess: ApmSourcesAccessPluginStart;
security?: SecurityPluginStart;
}
export interface ApmDataAccessPluginStart {
hasPrivileges: (params: Pick<ApmDataAccessPrivilegesCheck, 'request'>) => Promise<boolean>;
}
export interface ApmDataAccessServerDependencies {
security?: SecurityPluginStart;
}
export type ApmDataAccessServices = ReturnType<typeof getServices>;
export type { ApmDataAccessServicesParams } from './services/get_services';

View file

@ -8,7 +8,6 @@
"kbn_references": [
"@kbn/config-schema",
"@kbn/core",
"@kbn/i18n",
"@kbn/core-saved-objects-api-server",
"@kbn/data-plugin",
"@kbn/inspector-plugin",
@ -22,6 +21,7 @@
"@kbn/security-plugin-types-server",
"@kbn/utility-types",
"@kbn/elastic-agent-utils",
"@kbn/observability-utils-common"
"@kbn/observability-utils-common",
"@kbn/apm-sources-access-plugin"
]
}

Some files were not shown because too many files have changed in this diff Show more