[Metrics] metrics_data_access plugin (#164094)

## Summary

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

Creates a plugin providing utilities to access metrics data. The plugin
only exposes a server API which includes a client with two methods:
- `getMetricIndices` to retrieve the user-defined indices where metrics
are located
- `updateMetricIndices` to update the indices

The client is now used where we previously relied on infra plugin to
provide the configuration, in APM and Infra.

The plugin persists the configuration in a new saved object
`metrics-data-source`. Because this configuration was previously stored
in the `infrastructure-ui-source`, the plugin relies on a fallback to
reuse any existing value (see additional context
https://github.com/elastic/kibana/issues/161876#issuecomment-1673537400).

### Reviewers
There are no functional changes outside of Infra Monitoring UI and APM
UI, other codeowners are involved because this introduces a new saved
object
- APM - the change introduces a drop-in replacement of the
`infra.getMetricIndices` call. The ui code still relies on infra plugin
for a couple of components so we can't drop the dependency yet, those
we'll need to be moved to a tier 2 plugin (more details in
https://github.com/elastic/observability-dev/discussions/2787
(internal)) in a separate issue

### Testing
You'll need metrics data to verify data fetching works (I've used an
edge-oblt cluster)

1. Navigate to Infrastructure Settings and verify metric indices are
configured with the default value of `infrastructure-ui-source`
2. Update metric indices settings (if connected to oblt cluster add
`remote_cluster:..` indices)
3. Verify `metrics-data-source` saved object is persisted with correct
attributes
4. Verify Infrastructure Inventory is pulling data from the newly
configured indices
5. Go to APM services, verify service Infrastructure pulls data from
newly configured indices

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jason Rhodes <jason.rhodes@elastic.co>
This commit is contained in:
Kevin Lacabane 2023-09-14 12:55:13 +02:00 committed by GitHub
parent 688980ce34
commit d78ecfea34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 372 additions and 69 deletions

1
.github/CODEOWNERS vendored
View file

@ -495,6 +495,7 @@ x-pack/examples/third_party_maps_source_example @elastic/kibana-gis
src/plugins/maps_ems @elastic/kibana-gis
x-pack/plugins/maps @elastic/kibana-gis
x-pack/packages/maps/vector_tile_utils @elastic/kibana-gis
x-pack/plugins/metrics_data_access @elastic/infra-monitoring-ui
x-pack/packages/ml/agg_utils @elastic/ml-ui
x-pack/packages/ml/anomaly_utils @elastic/ml-ui
x-pack/packages/ml/category_validator @elastic/ml-ui

View file

@ -648,6 +648,10 @@ using the CURL scripts in the scripts folder.
|Visualize geo data from Elasticsearch or 3rd party geo-services.
|{kib-repo}blob/{branch}/x-pack/plugins/metrics_data_access/README.md[metricsDataAccess]
|Exposes utilities to access metrics data.
|{kib-repo}blob/{branch}/x-pack/plugins/ml/readme.md[ml]
|This plugin provides access to the machine learning features provided by
Elastic.

View file

@ -514,6 +514,7 @@
"@kbn/maps-ems-plugin": "link:src/plugins/maps_ems",
"@kbn/maps-plugin": "link:x-pack/plugins/maps",
"@kbn/maps-vector-tile-utils": "link:x-pack/packages/maps/vector_tile_utils",
"@kbn/metrics-data-access-plugin": "link:x-pack/plugins/metrics_data_access",
"@kbn/ml-agg-utils": "link:x-pack/packages/ml/agg_utils",
"@kbn/ml-anomaly-utils": "link:x-pack/packages/ml/anomaly_utils",
"@kbn/ml-category-validator": "link:x-pack/packages/ml/category_validator",

View file

@ -42,6 +42,10 @@
}
}
},
"metrics-data-source": {
"dynamic": false,
"properties": {}
},
"url": {
"dynamic": false,
"properties": {

View file

@ -105,6 +105,7 @@ const STANDARD_LIST_TYPES = [
'osquery-saved-query',
'osquery-pack',
'infrastructure-ui-source',
'metrics-data-source',
'metrics-explorer-view',
'inventory-view',
'infrastructure-monitoring-log-view',

View file

@ -115,6 +115,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"lens-ui-telemetry": "8c47a9e393861f76e268345ecbadfc8a5fb1e0bd",
"maintenance-window": "d893544460abad56ff7a0e25b78f78776dfe10d1",
"map": "76c71023bd198fb6b1163b31bafd926fe2ceb9da",
"metrics-data-source": "81b69dc9830699d9ead5ac8dcb9264612e2a3c89",
"metrics-explorer-view": "98cf395d0e87b89ab63f173eae16735584a8ff42",
"ml-job": "150e1ab260e87f9963cc99e013304b9c54703dab",
"ml-module": "2225cbb4bd508ea5f69db4b848be9d8a74b60198",

View file

@ -84,6 +84,7 @@ const previouslyRegisteredTypes = [
'maintenance-window',
'map',
'maps-telemetry',
'metrics-data-source',
'metrics-explorer-view',
'ml-job',
'ml-trained-model',

View file

@ -235,6 +235,7 @@ describe('split .kibana index into multiple system indices', () => {
"lens-ui-telemetry",
"maintenance-window",
"map",
"metrics-data-source",
"metrics-explorer-view",
"ml-job",
"ml-module",

View file

@ -984,6 +984,8 @@
"@kbn/maps-plugin/*": ["x-pack/plugins/maps/*"],
"@kbn/maps-vector-tile-utils": ["x-pack/packages/maps/vector_tile_utils"],
"@kbn/maps-vector-tile-utils/*": ["x-pack/packages/maps/vector_tile_utils/*"],
"@kbn/metrics-data-access-plugin": ["x-pack/plugins/metrics_data_access"],
"@kbn/metrics-data-access-plugin/*": ["x-pack/plugins/metrics_data_access/*"],
"@kbn/ml-agg-utils": ["x-pack/packages/ml/agg_utils"],
"@kbn/ml-agg-utils/*": ["x-pack/packages/ml/agg_utils/*"],
"@kbn/ml-anomaly-utils": ["x-pack/packages/ml/anomaly_utils"],

View file

@ -18,6 +18,7 @@
"logsShared",
"inspector",
"licensing",
"metricsDataAccess",
"observability",
"observabilityShared",
"exploratoryView",

View file

@ -6,8 +6,7 @@
*/
import { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types';
import { APMRouteHandlerResources } from '../../../../routes/apm_routes/register_apm_server_routes';
import { getInfraMetricIndices } from '../../get_infra_metric_indices';
import { APMRouteHandlerResources } from '../../../../routes/typings';
type InfraMetricsSearchParams = Omit<ESSearchRequest, 'index'> & {
size: number;
@ -17,6 +16,8 @@ type InfraMetricsSearchParams = Omit<ESSearchRequest, 'index'> & {
export type InfraMetricsClient = ReturnType<typeof createInfraMetricsClient>;
export function createInfraMetricsClient(resources: APMRouteHandlerResources) {
const metricsClient = resources.plugins.metricsDataAccess.setup.client;
return {
async search<TDocument, TParams extends InfraMetricsSearchParams>(
opts: TParams
@ -26,8 +27,7 @@ export function createInfraMetricsClient(resources: APMRouteHandlerResources) {
elasticsearch: { client: esClient },
} = await resources.context.core;
const indexName = await getInfraMetricIndices({
infraPlugin: resources.plugins.infra,
const indexName = await metricsClient.getMetricIndices({
savedObjectsClient,
});

View file

@ -54,7 +54,7 @@ import {
FleetSetupContract as FleetPluginSetup,
FleetStartContract as FleetPluginStart,
} from '@kbn/fleet-plugin/server';
import { InfraPluginStart, InfraPluginSetup } from '@kbn/infra-plugin/server';
import { MetricsDataPluginSetup } from '@kbn/metrics-data-access-plugin/server';
import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
import {
@ -79,7 +79,7 @@ export interface APMPluginSetupDependencies {
licensing: LicensingPluginSetup;
observability: ObservabilityPluginSetup;
ruleRegistry: RuleRegistryPluginSetupContract;
infra?: InfraPluginSetup;
metricsDataAccess: MetricsDataPluginSetup;
dataViews: {};
share: SharePluginSetup;
@ -105,7 +105,7 @@ export interface APMPluginStartDependencies {
licensing: LicensingPluginStart;
observability: undefined;
ruleRegistry: RuleRegistryPluginStartContract;
infra: InfraPluginStart;
metricsDataAccess: MetricsDataPluginSetup;
dataViews: DataViewsServerPluginStart;
share: undefined;

View file

@ -96,6 +96,7 @@
"@kbn/discover-plugin",
"@kbn/observability-ai-assistant-plugin",
"@kbn/apm-data-access-plugin",
"@kbn/metrics-data-access-plugin",
"@kbn/profiling-data-access-plugin",
"@kbn/profiling-utils",
"@kbn/core-analytics-server",

View file

@ -21,6 +21,7 @@
"fieldFormats",
"lens",
"logsShared",
"metricsDataAccess",
"observability",
"observabilityAIAssistant",
"observabilityShared",

View file

@ -8,6 +8,7 @@
import { i18n } from '@kbn/i18n';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
import { logViewSavedObjectName } from '@kbn/logs-shared-plugin/server';
import { metricsDataSourceSavedObjectName } from '@kbn/metrics-data-access-plugin/server';
import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants';
import { LOG_DOCUMENT_COUNT_RULE_TYPE_ID } from '../common/alerting/logs/log_threshold/types';
import {
@ -42,7 +43,7 @@ export const METRICS_FEATURE = {
catalogue: ['infraops', 'metrics'],
api: ['infra', 'rac'],
savedObject: {
all: ['infrastructure-ui-source'],
all: ['infrastructure-ui-source', metricsDataSourceSavedObjectName],
read: ['index-pattern'],
},
alerting: {
@ -64,7 +65,7 @@ export const METRICS_FEATURE = {
api: ['infra', 'rac'],
savedObject: {
all: [],
read: ['infrastructure-ui-source', 'index-pattern'],
read: ['infrastructure-ui-source', 'index-pattern', metricsDataSourceSavedObjectName],
},
alerting: {
rule: {

View file

@ -26,6 +26,7 @@ import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/serve
import { ObservabilityPluginSetup } from '@kbn/observability-plugin/server';
import { LogsSharedPluginSetup, LogsSharedPluginStart } from '@kbn/logs-shared-plugin/server';
import { VersionedRouteConfig } from '@kbn/core-http-server';
import { MetricsDataPluginSetup } from '@kbn/metrics-data-access-plugin/server';
export interface InfraServerPluginSetupDeps {
alerting: AlertingPluginContract;
@ -40,6 +41,7 @@ export interface InfraServerPluginSetupDeps {
visTypeTimeseries: VisTypeTimeseriesSetup;
ml?: MlPluginSetup;
logsShared: LogsSharedPluginSetup;
metricsDataAccess: MetricsDataPluginSetup;
}
export interface InfraServerPluginStartDeps {

View file

@ -18,6 +18,7 @@ import {
import { LifecycleAlertServices } from '@kbn/rule-registry-plugin/server';
import { ruleRegistryMocks } from '@kbn/rule-registry-plugin/server/mocks';
import { createLifecycleRuleExecutorMock } from '@kbn/rule-registry-plugin/server/utils/create_lifecycle_rule_executor_mock';
import { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server';
import {
Aggregators,
Comparator,
@ -1908,6 +1909,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
const mockLibs: any = {
sources: new InfraSources({
config: createMockStaticConfiguration({}),
metricsClient: {
getMetricIndices: jest.fn().mockResolvedValue('metrics-*,metricbeat-*'),
} as unknown as MetricsDataClient,
}),
configuration: createMockStaticConfiguration({}),
metricsRules: {

View file

@ -12,6 +12,7 @@ import type { AlertsLocatorParams } from '@kbn/observability-plugin/common';
import { ObservabilityConfig } from '@kbn/observability-plugin/server';
import type { LocatorPublic } from '@kbn/share-plugin/common';
import type { ILogsSharedLogEntriesDomain } from '@kbn/logs-shared-plugin/server';
import type { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server';
import { RulesServiceSetup } from '../services/rules';
import { InfraConfig, InfraPluginStartServicesAccessor } from '../types';
import { KibanaFramework } from './adapters/framework/kibana_framework_adapter';
@ -39,4 +40,5 @@ export interface InfraBackendLibs extends InfraDomainLibs {
handleEsError: typeof handleEsError;
logger: Logger;
alertsLocator?: LocatorPublic<AlertsLocatorParams>;
metricsClient: MetricsDataClient;
}

View file

@ -1,30 +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 { savedObjectsClientMock } from '@kbn/core/server/mocks';
import { defaultSourceConfiguration, InfraSource } from '../sources';
import { createInfraSourcesMock } from '../sources/mocks';
import { makeGetMetricIndices } from './make_get_metric_indices';
describe('getMetricIndices', () => {
it('should return the indices from a resolved configuration', async () => {
const sourceConfiguration: InfraSource = {
id: 'default',
origin: 'stored',
configuration: defaultSourceConfiguration,
};
const infraSourcesMock = createInfraSourcesMock();
infraSourcesMock.getSourceConfiguration.mockResolvedValueOnce(sourceConfiguration);
const getMetricIndices = makeGetMetricIndices(infraSourcesMock);
const savedObjectsClient = savedObjectsClientMock.create();
const metricIndices = await getMetricIndices(savedObjectsClient);
expect(metricIndices).toEqual(defaultSourceConfiguration.metricAlias);
});
});

View file

@ -1,16 +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 { SavedObjectsClientContract } from '@kbn/core/server';
import type { IInfraSources } from '../sources';
export function makeGetMetricIndices(metricSources: IInfraSources) {
return async (savedObjectsClient: SavedObjectsClientContract, sourceId: string = 'default') => {
const source = await metricSources.getSourceConfiguration(savedObjectsClient, sourceId);
return source.configuration.metricAlias;
};
}

View file

@ -10,6 +10,7 @@ import type { InfraSources } from './sources';
type IInfraSources = Pick<InfraSources, keyof InfraSources>;
export const createInfraSourcesMock = (): jest.Mocked<IInfraSources> => ({
getInfraSourceConfiguration: jest.fn(),
getSourceConfiguration: jest.fn(),
createSourceConfiguration: jest.fn(),
deleteSourceConfiguration: jest.fn(),

View file

@ -6,6 +6,7 @@
*/
import { SavedObject } from '@kbn/core/server';
import { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server';
import { InfraConfig } from '../../types';
import { infraSourceConfigurationSavedObjectName } from './saved_object_type';
import { InfraSources } from './sources';
@ -15,6 +16,7 @@ describe('the InfraSources lib', () => {
test('returns a source configuration if it exists', async () => {
const sourcesLib = new InfraSources({
config: createMockStaticConfiguration({}),
metricsClient: createMockMetricsDataClient('METRIC_ALIAS'),
});
const request: any = createRequestContext({
@ -56,6 +58,7 @@ describe('the InfraSources lib', () => {
logIndices: { type: 'index_pattern', indexPatternId: 'LOG_ALIAS' },
},
}),
metricsClient: createMockMetricsDataClient('METRIC_ALIAS'),
});
const request: any = createRequestContext({
@ -83,6 +86,7 @@ describe('the InfraSources lib', () => {
test('adds missing attributes from the default configuration to a source configuration', async () => {
const sourcesLib = new InfraSources({
config: createMockStaticConfiguration({}),
metricsClient: createMockMetricsDataClient(),
});
const request: any = createRequestContext({
@ -128,6 +132,12 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
enabled: true,
});
const createMockMetricsDataClient = (metricAlias: string = 'metrics-*,metricbeat-*') =>
({
getMetricIndices: jest.fn().mockResolvedValue(metricAlias),
updateMetricIndices: jest.fn(),
} as unknown as MetricsDataClient);
const createRequestContext = (savedObject?: SavedObject<unknown>) => {
return {
core: {

View file

@ -15,6 +15,7 @@ import {
SavedObjectsClientContract,
SavedObjectsErrorHelpers,
} from '@kbn/core/server';
import { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server';
import {
InfraSavedSourceConfiguration,
InfraSource,
@ -35,6 +36,7 @@ import { infraSourceConfigurationSavedObjectName } from './saved_object_type';
interface Libs {
config: InfraConfig;
metricsClient: MetricsDataClient;
}
// extract public interface
@ -48,7 +50,7 @@ export class InfraSources {
this.libs = libs;
}
public async getSourceConfiguration(
public async getInfraSourceConfiguration(
savedObjectsClient: SavedObjectsClientContract,
sourceId: string
): Promise<InfraSource> {
@ -90,6 +92,21 @@ export class InfraSources {
return savedSourceConfiguration;
}
public async getSourceConfiguration(
savedObjectsClient: SavedObjectsClientContract,
sourceId: string
): Promise<InfraSource> {
const sourceConfiguration = await this.getInfraSourceConfiguration(
savedObjectsClient,
sourceId
);
const metricAlias = await this.libs.metricsClient.getMetricIndices({
savedObjectsClient,
});
sourceConfiguration.configuration.metricAlias = metricAlias;
return sourceConfiguration;
}
public async getAllSourceConfigurations(savedObjectsClient: SavedObjectsClientContract) {
const staticDefaultSourceConfiguration = await this.getStaticDefaultSourceConfiguration();
@ -129,6 +146,11 @@ export class InfraSources {
})
);
await this.libs.metricsClient.updateMetricIndices({
savedObjectsClient,
metricIndices: newSourceConfiguration.metricAlias,
});
return {
...createdSourceConfiguration,
configuration: mergeSourceConfiguration(
@ -180,6 +202,11 @@ export class InfraSources {
})
);
await this.libs.metricsClient.updateMetricIndices({
savedObjectsClient,
metricIndices: updatedSourceConfiguration.configuration.metricAlias!,
});
return {
...updatedSourceConfiguration,
configuration: mergeSourceConfiguration(

View file

@ -27,7 +27,6 @@ const createInfraSetupMock = () => {
const createInfraStartMock = () => {
const infraStartMock: jest.Mocked<InfraPluginStart> = {
getMetricIndices: jest.fn(),
inventoryViews: createInventoryViewsServiceStartMock(),
metricsExplorerViews: createMetricsExplorerViewsServiceStartMock(),
};

View file

@ -18,6 +18,7 @@ import { i18n } from '@kbn/i18n';
import { Logger } from '@kbn/logging';
import { alertsLocatorID } from '@kbn/observability-plugin/common';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
import { GetMetricIndicesOptions } from '@kbn/metrics-data-access-plugin/server';
import {
DISCOVER_APP_TARGET,
LOGS_APP_TARGET,
@ -41,7 +42,6 @@ import {
import { InfraFieldsDomain } from './lib/domains/fields_domain';
import { InfraMetricsDomain } from './lib/domains/metrics_domain';
import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types';
import { makeGetMetricIndices } from './lib/metrics/make_get_metric_indices';
import { infraSourceConfigurationSavedObjectType, InfraSources } from './lib/sources';
import { InfraSourceStatus } from './lib/source_status';
import { inventoryViewSavedObjectType, metricsExplorerViewSavedObjectType } from './saved_objects';
@ -153,8 +153,17 @@ export class InfraServerPlugin
setup(core: InfraPluginCoreSetup, plugins: InfraServerPluginSetupDeps) {
const framework = new KibanaFramework(core, this.config, plugins);
const metricsClient = plugins.metricsDataAccess.client;
metricsClient.setDefaultMetricIndicesHandler(async (options: GetMetricIndicesOptions) => {
const sourceConfiguration = await sources.getInfraSourceConfiguration(
options.savedObjectsClient,
'default'
);
return sourceConfiguration.configuration.metricAlias;
});
const sources = new InfraSources({
config: this.config,
metricsClient,
});
const sourceStatus = new InfraSourceStatus(
new InfraElasticsearchSourceStatusAdapter(framework),
@ -186,6 +195,7 @@ export class InfraServerPlugin
framework,
sources,
sourceStatus,
metricsClient,
...domainLibs,
handleEsError,
logsRules: this.logsRules.setup(core, plugins),
@ -202,7 +212,7 @@ export class InfraServerPlugin
// Register an handler to retrieve the fallback logView starting from a source configuration
plugins.logsShared.logViews.registerLogViewFallbackHandler(async (sourceId, { soClient }) => {
const sourceConfiguration = await sources.getSourceConfiguration(soClient, sourceId);
const sourceConfiguration = await sources.getInfraSourceConfiguration(soClient, sourceId);
return mapSourceToLogView(sourceConfiguration);
});
plugins.logsShared.logViews.setLogViewsStaticConfig({
@ -270,7 +280,6 @@ export class InfraServerPlugin
return {
inventoryViews,
metricsExplorerViews,
getMetricIndices: makeGetMetricIndices(this.libs.sources),
};
}

View file

@ -5,11 +5,7 @@
* 2.0.
*/
import type {
CoreSetup,
CustomRequestHandlerContext,
SavedObjectsClientContract,
} from '@kbn/core/server';
import type { CoreSetup, CustomRequestHandlerContext } from '@kbn/core/server';
import type { SearchRequestHandlerContext } from '@kbn/data-plugin/server';
import type { MlPluginSetup } from '@kbn/ml-plugin/server';
import type { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration';
@ -37,10 +33,6 @@ export interface InfraPluginSetup {
export interface InfraPluginStart {
inventoryViews: InventoryViewsServiceStart;
metricsExplorerViews: MetricsExplorerViewsServiceStart;
getMetricIndices: (
savedObjectsClient: SavedObjectsClientContract,
sourceId?: string
) => Promise<string>;
}
export type MlSystem = ReturnType<MlPluginSetup['mlSystemProvider']>;

View file

@ -70,6 +70,7 @@
"@kbn/licensing-plugin",
"@kbn/aiops-utils",
"@kbn/lens-embeddable-utils",
"@kbn/metrics-data-access-plugin",
"@kbn/expressions-plugin"
],
"exclude": ["target/**/*"]

View file

@ -0,0 +1,3 @@
# Metrics Data Access
Exposes utilities to access metrics data.

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/x-pack/plugins/metrics_data_access'],
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/metrics_data_access',
coverageReporters: ['text', 'html'],
collectCoverageFrom: ['<rootDir>/x-pack/plugins/metrics_data/{server}/**/*.test.ts'],
};

View file

@ -0,0 +1,15 @@
{
"type": "plugin",
"id": "@kbn/metrics-data-access-plugin",
"owner": "@elastic/infra-monitoring-ui",
"description": "Exposes utilities for accessing metrics data",
"plugin": {
"id": "metricsDataAccess",
"server": true,
"browser": false,
"configPath": ["xpack", "metrics_data_access"],
"requiredPlugins": [],
"requiredBundles": [],
"extraPublicDirs": []
}
}

View file

@ -0,0 +1,55 @@
/*
* 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 { SavedObjectsErrorHelpers } from '@kbn/core/server';
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import { MetricsDataClient } 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 savedObjectsClient = {
get: jest.fn().mockResolvedValue({ attributes: { metricIndices: 'foo,bar' } }),
};
const indices = await client.getMetricIndices({
savedObjectsClient: savedObjectsClient as unknown as SavedObjectsClientContract,
});
expect(savedObjectsClient.get.mock.calls.length).toEqual(1);
expect(savedObjectsClient.get.mock.calls[0]).toEqual([
metricsDataSourceSavedObjectName,
'default',
]);
expect(indices).toEqual('foo,bar');
});
it('falls back to provided handler when no metrics saved object exists', async () => {
const savedObjectsClient = {
get: jest.fn().mockRejectedValue(SavedObjectsErrorHelpers.createGenericNotFoundError()),
};
const indices = await client.getMetricIndices({
savedObjectsClient: savedObjectsClient as unknown as SavedObjectsClientContract,
});
expect(savedObjectsClient.get.mock.calls.length).toEqual(1);
expect(savedObjectsClient.get.mock.calls[0]).toEqual([
metricsDataSourceSavedObjectName,
'default',
]);
expect(indices).toEqual('fallback-indices*');
});
});
});

View file

@ -0,0 +1,55 @@
/*
* 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 { SavedObjectsErrorHelpers } from '@kbn/core/server';
import {
DefaultMetricIndicesHandler,
GetMetricIndicesOptions,
UpdateMetricIndicesOptions,
} from '../types';
import {
MetricsDataSavedObject,
metricsDataSourceSavedObjectName,
} from '../saved_objects/metrics_data_source';
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);
}
throw err;
});
return metricIndices;
}
async updateMetricIndices(options: UpdateMetricIndicesOptions) {
const object = await options.savedObjectsClient.create(
metricsDataSourceSavedObjectName,
{
metricIndices: options.metricIndices,
},
{ id: this.defaultSavedObjectId, overwrite: true }
);
return object;
}
setDefaultMetricIndicesHandler(handler: DefaultMetricIndicesHandler) {
this.getDefaultMetricIndices = handler;
}
}

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { MetricsDataClient } from './client';

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 { PluginInitializerContext } from '@kbn/core/server';
import { MetricsDataPlugin } from './plugin';
export type {
MetricsDataPluginSetup,
GetMetricIndicesOptions,
UpdateMetricIndicesOptions,
DefaultMetricIndicesHandler,
} from './types';
export { metricsDataSourceSavedObjectName } from './saved_objects/metrics_data_source';
export { MetricsDataClient } from './client';
export function plugin(context: PluginInitializerContext) {
return new MetricsDataPlugin(context);
}

View file

@ -0,0 +1,32 @@
/*
* 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 { CoreSetup, PluginInitializerContext, Plugin } from '@kbn/core/server';
import { MetricsDataPluginSetup } from './types';
import { MetricsDataClient } from './client';
import { metricsDataSourceSavedObjectType } from './saved_objects/metrics_data_source';
export class MetricsDataPlugin implements Plugin<MetricsDataPluginSetup, {}, {}, {}> {
private metricsClient: MetricsDataClient | null = null;
constructor(context: PluginInitializerContext) {}
public setup(core: CoreSetup) {
core.savedObjects.registerType(metricsDataSourceSavedObjectType);
this.metricsClient = new MetricsDataClient();
return {
client: this.metricsClient,
};
}
public start() {
return {};
}
public stop() {}
}

View file

@ -0,0 +1,33 @@
/*
* 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 { SavedObject, SavedObjectsType } from '@kbn/core/server';
export const metricsDataSourceSavedObjectName = 'metrics-data-source';
export interface MetricsDataSavedObject {
metricIndices: string;
}
export const metricsDataSourceSavedObjectType: SavedObjectsType = {
name: metricsDataSourceSavedObjectName,
hidden: false,
namespaceType: 'single',
management: {
defaultSearchField: 'name',
displayName: 'metrics data source',
getTitle(savedObject: SavedObject<MetricsDataSavedObject>) {
return `Metrics data source [id=${savedObject.id}]`;
},
icon: 'metricsApp',
importableAndExportable: true,
},
mappings: {
dynamic: false,
properties: {},
},
};

View file

@ -0,0 +1,26 @@
/*
* 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-saved-objects-api-server';
import { MetricsDataClient } from './client';
export interface MetricsDataPluginSetup {
client: MetricsDataClient;
}
export interface GetMetricIndicesOptions {
savedObjectsClient: SavedObjectsClientContract;
}
export type UpdateMetricIndicesOptions = GetMetricIndicesOptions & {
metricIndices: string;
};
export type DefaultMetricIndicesHandler =
| ((options: GetMetricIndicesOptions) => Promise<string>)
| null;

View file

@ -0,0 +1,12 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": ["../../../typings/**/*", "server/**/*"],
"exclude": ["target/**/*"],
"kbn_references": [
"@kbn/core",
"@kbn/core-saved-objects-api-server",
]
}

View file

@ -4910,6 +4910,10 @@
version "0.0.0"
uid ""
"@kbn/metrics-data-access-plugin@link:x-pack/plugins/metrics_data_access":
version "0.0.0"
uid ""
"@kbn/ml-agg-utils@link:x-pack/packages/ml/agg_utils":
version "0.0.0"
uid ""