From 495440360807431cfc2e43f4fca2a0e37386f80b Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Thu, 12 Aug 2021 20:23:38 +0200 Subject: [PATCH] Address code review feedback --- .../server/lib/alerts/register_apm_alerts.ts | 4 +- .../apm/server/lib/alerts/test_utils/index.ts | 5 +- .../server/lib/services/get_service_alerts.ts | 4 +- x-pack/plugins/apm/server/plugin.ts | 3 - x-pack/plugins/apm/server/routes/typings.ts | 4 +- .../server/services/rules/rule_data_client.ts | 3 - .../infra/server/services/rules/types.ts | 4 +- .../server/routes/register_routes.ts | 4 +- .../observability/server/routes/types.ts | 4 +- .../rule_data_client/rule_data_client.ts | 48 +-- .../rule_data_plugin_service/index_info.ts | 135 ++++++++ .../rule_data_plugin_service/index_names.ts | 79 ----- .../rule_data_plugin_service/index_options.ts | 112 ++++++- .../resource_installer.ts | 293 +++++++++--------- .../resource_names.ts | 29 -- .../rule_data_plugin_service.mock.ts | 2 +- .../rule_data_plugin_service.ts | 116 ++++--- .../server/rule_data_plugin_service/utils.ts | 12 +- .../server/utils/create_lifecycle_executor.ts | 4 +- .../create_lifecycle_rule_type_factory.ts | 4 +- .../server/utils/persistence_types.ts | 4 +- .../utils/with_rule_data_client_factory.ts | 8 +- .../routes/index/create_index_route.ts | 2 +- .../routes/rules/create_rules_route.ts | 4 +- .../routes/rules/delete_rules_route.ts | 4 +- .../routes/rules/find_rules_route.ts | 4 +- .../routes/rules/patch_rules_route.ts | 4 +- .../routes/rules/read_rules_route.ts | 4 +- .../routes/rules/update_rules_route.ts | 4 +- .../rule_registry_log_client.ts | 7 +- .../rule_types/__mocks__/rule_type.ts | 5 +- .../create_indicator_match_alert_type.test.ts | 3 - .../create_indicator_match_alert_type.ts | 2 - .../lib/detection_engine/rule_types/types.ts | 6 +- .../security_solution/server/plugin.ts | 7 +- .../security_solution/server/routes/index.ts | 4 +- .../server/lib/alerts/test_utils/index.ts | 6 +- x-pack/plugins/uptime/server/plugin.ts | 3 - x-pack/plugins/uptime/server/uptime_server.ts | 4 +- .../tests/alerts/rule_registry.ts | 3 +- 40 files changed, 552 insertions(+), 405 deletions(-) create mode 100644 x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_info.ts delete mode 100644 x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_names.ts delete mode 100644 x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_names.ts diff --git a/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts b/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts index 022fad6fa7840ce..db79b4f11df29e3 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_apm_alerts.ts @@ -8,7 +8,7 @@ import { Observable } from 'rxjs'; import { Logger } from 'kibana/server'; import { PluginSetupContract as AlertingPluginSetupContract } from '../../../../alerting/server'; -import { RuleDataClient } from '../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../rule_registry/server'; import { registerTransactionDurationAlertType } from './register_transaction_duration_alert_type'; import { registerTransactionDurationAnomalyAlertType } from './register_transaction_duration_anomaly_alert_type'; import { registerErrorCountAlertType } from './register_error_count_alert_type'; @@ -17,7 +17,7 @@ import { MlPluginSetup } from '../../../../ml/server'; import { registerTransactionErrorRateAlertType } from './register_transaction_error_rate_alert_type'; export interface RegisterRuleDependencies { - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; ml?: MlPluginSetup; alerting: AlertingPluginSetupContract; config$: Observable; diff --git a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts index 679f33707b5b531..26667dff90c6ea5 100644 --- a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts +++ b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts @@ -8,7 +8,7 @@ import { Logger } from 'kibana/server'; import { of } from 'rxjs'; import { elasticsearchServiceMock } from 'src/core/server/mocks'; -import type { RuleDataClient } from '../../../../../rule_registry/server'; +import type { IRuleDataClient } from '../../../../../rule_registry/server'; import { PluginSetupContract as AlertingPluginSetupContract } from '../../../../../alerting/server'; import { APMConfig, APM_SERVER_FEATURE_ID } from '../../..'; @@ -63,7 +63,8 @@ export const createRuleTypeMocks = () => { }; }, isWriteEnabled: jest.fn(() => true), - } as unknown) as RuleDataClient, + indexName: '.alerts-observability.apm.alerts', + } as unknown) as IRuleDataClient, }, services, scheduleActions, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts b/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts index 4cbc62d87eff628..af756b61ac0d87d 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_alerts.ts @@ -8,7 +8,7 @@ import type { EVENT_KIND as EVENT_KIND_TYPED } from '@kbn/rule-data-utils'; // @ts-expect-error import { EVENT_KIND as EVENT_KIND_NON_TYPED } from '@kbn/rule-data-utils/target_node/technical_field_names'; -import { RuleDataClient } from '../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../rule_registry/server'; import { SERVICE_NAME, TRANSACTION_TYPE, @@ -26,7 +26,7 @@ export async function getServiceAlerts({ environment, transactionType, }: { - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; start: number; end: number; serviceName: string; diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 5fc3f5e36b49165..807d21768a50c9d 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -119,9 +119,6 @@ export class APMPlugin { name: 'mappings', version: 0, - settings: { - number_of_shards: 1, - }, mappings: mappingFromFieldMap( { [SERVICE_NAME]: { diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 4279cfd84328c76..76f19a6a0ca3e63 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -12,7 +12,7 @@ import { KibanaRequest, CoreStart, } from 'src/core/server'; -import { RuleDataClient } from '../../../rule_registry/server'; +import { IRuleDataClient } from '../../../rule_registry/server'; import { AlertingApiRequestHandlerContext } from '../../../alerting/server'; import type { RacApiRequestHandlerContext } from '../../../rule_registry/server'; import { LicensingApiRequestHandlerContext } from '../../../licensing/server'; @@ -72,6 +72,6 @@ export interface APMRouteHandlerResources { start: () => Promise[key]['start']>; }; }; - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; telemetryUsageCounter?: TelemetryUsageCounter; } diff --git a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts index 6c67ee7167bfddc..9b3f7edb97007c9 100644 --- a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts +++ b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts @@ -32,9 +32,6 @@ export const createRuleDataClient = ({ { name: 'mappings', version: 0, - settings: { - number_of_shards: 1, - }, mappings: {}, }, ], diff --git a/x-pack/plugins/infra/server/services/rules/types.ts b/x-pack/plugins/infra/server/services/rules/types.ts index b67b79ee5d3c247..daa44381650e07e 100644 --- a/x-pack/plugins/infra/server/services/rules/types.ts +++ b/x-pack/plugins/infra/server/services/rules/types.ts @@ -8,7 +8,7 @@ import { PluginSetupContract as AlertingPluginSetup } from '../../../../alerting/server'; import { createLifecycleExecutor, - RuleDataClient, + IRuleDataClient, RuleRegistryPluginSetupContract, } from '../../../../rule_registry/server'; @@ -24,7 +24,7 @@ export interface RulesServiceStartDeps {} export interface RulesServiceSetup { createLifecycleRuleExecutor: LifecycleRuleExecutorCreator; - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; } // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/x-pack/plugins/observability/server/routes/register_routes.ts b/x-pack/plugins/observability/server/routes/register_routes.ts index 75b6703cc64dec9..43b47f26dcdd146 100644 --- a/x-pack/plugins/observability/server/routes/register_routes.ts +++ b/x-pack/plugins/observability/server/routes/register_routes.ts @@ -13,7 +13,7 @@ import { import { CoreSetup, CoreStart, Logger, RouteRegistrar } from 'kibana/server'; import Boom from '@hapi/boom'; import { RequestAbortedError } from '@elastic/elasticsearch/lib/errors'; -import { RuleDataClient } from '../../../rule_registry/server'; +import { IRuleDataClient } from '../../../rule_registry/server'; import { ObservabilityRequestHandlerContext } from '../types'; import { AbstractObservabilityServerRouteRepository } from './types'; @@ -29,7 +29,7 @@ export function registerRoutes({ }; repository: AbstractObservabilityServerRouteRepository; logger: Logger; - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; }) { const routes = repository.getRoutes(); diff --git a/x-pack/plugins/observability/server/routes/types.ts b/x-pack/plugins/observability/server/routes/types.ts index 1fa7229c6cf6283..15a3274087d0ed7 100644 --- a/x-pack/plugins/observability/server/routes/types.ts +++ b/x-pack/plugins/observability/server/routes/types.ts @@ -12,7 +12,7 @@ import type { ServerRouteRepository, } from '@kbn/server-route-repository'; import { CoreSetup, CoreStart, KibanaRequest, Logger } from 'kibana/server'; -import { RuleDataClient } from '../../../rule_registry/server'; +import { IRuleDataClient } from '../../../rule_registry/server'; import { ObservabilityServerRouteRepository } from './get_global_observability_server_route_repository'; import { ObservabilityRequestHandlerContext } from '../types'; @@ -24,7 +24,7 @@ export interface ObservabilityRouteHandlerResources { start: () => Promise; setup: CoreSetup; }; - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; request: KibanaRequest; context: ObservabilityRequestHandlerContext; logger: Logger; diff --git a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts index 0e44bf25f645d33..a0064988e1a74d8 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts @@ -6,38 +6,28 @@ */ import { ResponseError } from '@elastic/elasticsearch/lib/errors'; -import { ValidFeatureId } from '@kbn/rule-data-utils/target/alerts_as_data_rbac'; import { ElasticsearchClient } from 'kibana/server'; import { IndexPatternsFetcher } from '../../../../../src/plugins/data/server'; import { RuleDataWriteDisabledError } from '../rule_data_plugin_service/errors'; -import { IndexNames } from '../rule_data_plugin_service/index_names'; -import { IndexOptions } from '../rule_data_plugin_service/index_options'; +import { IndexInfo } from '../rule_data_plugin_service/index_info'; import { ResourceInstaller } from '../rule_data_plugin_service/resource_installer'; import { IRuleDataClient, IRuleDataReader, IRuleDataWriter } from './types'; -/** - * The purpose of the `feature` param is to force the user to update - * the data structure which contains the mapping of consumers to alerts - * as data indices. The idea is it is typed such that it forces the - * user to go to the code and modify it. At least until a better system - * is put in place or we move the alerts as data client out of rule registry. - */ interface ConstructorOptions { - feature: ValidFeatureId; - indexNames: IndexNames; - indexOptions: IndexOptions; + indexInfo: IndexInfo; resourceInstaller: ResourceInstaller; isWriteEnabled: boolean; - waitUntilIndexIsReady: () => Promise<{ clusterClient: ElasticsearchClient }>; + waitUntilReadyForReading: Promise; + waitUntilReadyForWriting: Promise; } export class RuleDataClient implements IRuleDataClient { constructor(private readonly options: ConstructorOptions) {} public get indexName(): string { - return this.options.indexNames.baseName; + return this.options.indexInfo.baseName; } public isWriteEnabled(): boolean { @@ -45,45 +35,41 @@ export class RuleDataClient implements IRuleDataClient { } public getReader(options: { namespace?: string } = {}): IRuleDataReader { - const { indexNames, waitUntilIndexIsReady } = this.options; - - // Because namespace is user-defined in general, by default we want to - // ignore namespace when reading, and search over all the namespaces. - const namespace = options.namespace || '*'; - const indexAliasOrPattern = indexNames.getPrimaryAlias(namespace); + const { indexInfo, waitUntilReadyForReading } = this.options; + const indexPattern = indexInfo.getPatternForReading(options.namespace); return { search: async (request) => { - const { clusterClient } = await waitUntilIndexIsReady(); + const clusterClient = await waitUntilReadyForReading; const { body } = (await clusterClient.search({ ...request, - index: indexAliasOrPattern, + index: indexPattern, })) as { body: any }; return body; }, getDynamicIndexPattern: async () => { - const { clusterClient } = await waitUntilIndexIsReady(); + const clusterClient = await waitUntilReadyForReading; const indexPatternsFetcher = new IndexPatternsFetcher(clusterClient); try { const fields = await indexPatternsFetcher.getFieldsForWildcard({ - pattern: indexAliasOrPattern, + pattern: indexPattern, }); return { fields, timeFieldName: '@timestamp', - title: indexAliasOrPattern, + title: indexPattern, }; } catch (err) { if (err.output?.payload?.code === 'no_matching_indices') { return { fields: [], timeFieldName: '@timestamp', - title: indexAliasOrPattern, + title: indexPattern, }; } throw err; @@ -93,10 +79,10 @@ export class RuleDataClient implements IRuleDataClient { } public getWriter(options: { namespace?: string } = {}): IRuleDataWriter { - const { indexNames, indexOptions, resourceInstaller, waitUntilIndexIsReady } = this.options; + const { indexInfo, resourceInstaller, waitUntilReadyForWriting } = this.options; const namespace = options.namespace || 'default'; - const alias = indexNames.getPrimaryAlias(namespace); + const alias = indexInfo.getPrimaryAlias(namespace); const isWriteEnabled = this.isWriteEnabled(); return { @@ -105,7 +91,7 @@ export class RuleDataClient implements IRuleDataClient { throw new RuleDataWriteDisabledError(); } - const { clusterClient } = await waitUntilIndexIsReady(); + const clusterClient = await waitUntilReadyForWriting; const requestWithDefaultParameters = { ...request, @@ -125,7 +111,7 @@ export class RuleDataClient implements IRuleDataClient { )) ) { return resourceInstaller - .createWriteTargetIfNeeded(indexOptions, indexNames, namespace) + .installNamespaceLevelResources(indexInfo, namespace) .then(() => { return clusterClient.bulk(requestWithDefaultParameters).then((retryResponse) => { if (retryResponse.body.errors) { diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_info.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_info.ts new file mode 100644 index 000000000000000..4e64ea025a27ae3 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_info.ts @@ -0,0 +1,135 @@ +/* + * 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 { IndexOptions } from './index_options'; +import { joinWithDash } from './utils'; + +interface ConstructorOptions { + /** + * Prepends a relative resource name (defined in the code) with + * a full resource prefix, which starts with '.alerts' and can + * optionally include a user-defined part in it. + * @example 'security.alerts' => '.alerts-security.alerts' + */ + getResourceName(relativeName: string): string; + + /** + * Options provided by the plugin/solution defining the index. + */ + indexOptions: IndexOptions; +} + +/** + * Internal info used by the index bootstrapping logic, reader and writer. + * Should not be exposed to clients of the library. + * + * Names returned by methods of this class should be used in Elasticsearch APIs. + */ +export class IndexInfo { + constructor(options: ConstructorOptions) { + const { getResourceName, indexOptions } = options; + const { registrationContext, dataset } = indexOptions; + + this.indexOptions = indexOptions; + this.baseName = getResourceName(`${registrationContext}.${dataset}`); + this.basePattern = joinWithDash(this.baseName, '*'); + } + + /** + * Options provided by the plugin/solution defining the index. + */ + public readonly indexOptions: IndexOptions; + + /** + * Base index name, prefixed with the full resource prefix. + * @example '.alerts-security.alerts' + */ + public readonly baseName: string; + + /** + * Base index pattern. Includes all namespaces of this index. + * @example '.alerts-security.alerts-*' + */ + public readonly basePattern: string; + + /** + * Primary index alias. Includes a namespace. + * Used as a write target when writing documents to the index. + * @example '.alerts-security.alerts-default' + */ + public getPrimaryAlias(namespace: string): string { + return joinWithDash(this.baseName, namespace); + } + + /** + * Index pattern based on the primary alias. + * @example '.alerts-security.alerts-default-*' + */ + public getPrimaryAliasPattern(namespace: string): string { + return joinWithDash(this.baseName, namespace, '*'); + } + + /** + * Optional secondary alias that can be applied to concrete indices in + * addition to the primary one. + * @example '.siem-signals-default', null + */ + public getSecondaryAlias(namespace: string): string | null { + const { secondaryAlias } = this.indexOptions; + return secondaryAlias ? joinWithDash(secondaryAlias, namespace) : null; + } + + /** + * Index pattern that should be used when reading documents from the index. + * Can include or exclude the namespace. + * + * IMPORTANT: The namespace is user-defined in general. Because of that, when + * reading data from the index, we want to do this by default: + * - pass namespace = undefined + * - search over all the namespaces + * - include nested registration contexts eagerly + * - e.g. if baseName='.alerts-observability', include '.alerts-observability.apm' + * + * @example '.alerts-security.alerts-default*', '.alerts-security.alerts*' + */ + public getPatternForReading(namespace?: string): string { + return `${joinWithDash(this.baseName, namespace)}*`; + } + + /** + * Name of the initial concrete index, with the namespace and the ILM suffix. + * @example '.alerts-security.alerts-default-000001' + */ + public getConcreteIndexInitialName(namespace: string): string { + return joinWithDash(this.baseName, namespace, '000001'); + } + + /** + * Name of the custom ILM policy (if it's provided by the plugin/solution). + * Specific to the index. Shared between all namespaces of the index. + * @example '.alerts-security.alerts-policy' + */ + public getIlmPolicyName(): string { + return joinWithDash(this.baseName, 'policy'); + } + + /** + * Full name of a component template. + * @example '.alerts-security.alerts-mappings' + */ + public getComponentTemplateName(relativeName: string): string { + return joinWithDash(this.baseName, relativeName); + } + + /** + * Full name of the index template. Each namespace gets its own template. + * @example '.alerts-security.alerts-default-index-template' + */ + public getIndexTemplateName(namespace: string): string { + return joinWithDash(this.baseName, namespace, 'index-template'); + } +} diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_names.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_names.ts deleted file mode 100644 index 7bbfb8f90db1683..000000000000000 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_names.ts +++ /dev/null @@ -1,79 +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 { Dataset } from './index_options'; -import { ResourceNames } from './resource_names'; - -const joinWithDash = ResourceNames.joinWithDash; - -interface ConstructorOptions { - resourceNames: ResourceNames; - - /** @example 'security', 'observability', 'observability.logs' */ - registrationContext: string; - - /** @example 'alerts', 'events' */ - dataset: Dataset; - - /** @example '.siem-signals', null */ - secondaryAlias: string | null; -} - -export class IndexNames { - constructor(private readonly options: ConstructorOptions) { - const { resourceNames, registrationContext, dataset } = options; - - this.prefix = resourceNames.getFullPrefix(); - this.baseName = resourceNames.getFullName(registrationContext, dataset); - this.basePattern = joinWithDash(this.baseName, '*'); - } - - /** @example '.alerts' */ - public readonly prefix: string; - - /** @example '.alerts-security.alerts' */ - public readonly baseName: string; - - /** @example '.alerts-security.alerts-*' */ - public readonly basePattern: string; - - /** @example '.alerts-security.alerts-default' */ - public getPrimaryAlias(namespace: string): string { - return joinWithDash(this.baseName, namespace); - } - - /** @example '.alerts-security.alerts-default-*' */ - public getPrimaryAliasPattern(namespace: string): string { - return joinWithDash(this.baseName, namespace, '*'); - } - - /** @example '.siem-signals-default', null */ - public getSecondaryAlias(namespace: string): string | null { - const { secondaryAlias } = this.options; - return secondaryAlias ? joinWithDash(secondaryAlias, namespace) : null; - } - - /** @example '.alerts-security.alerts-default-000001' */ - public getConcreteIndexInitialName(namespace: string): string { - return joinWithDash(this.baseName, namespace, '000001'); - } - - /** @example '.alerts-security.alerts-policy' */ - public getIlmPolicyName(): string { - return joinWithDash(this.baseName, 'policy'); - } - - /** @example '.alerts-security.alerts-mappings' */ - public getComponentTemplateName(relativeName: string): string { - return joinWithDash(this.baseName, relativeName); - } - - /** @example '.alerts-security.alerts-default-index-template' */ - public getIndexTemplateName(namespace: string): string { - return joinWithDash(this.baseName, namespace, 'index-template'); - } -} diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_options.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_options.ts index d6fe95510650f67..0f9486a068c2442 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_options.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_options.ts @@ -8,19 +8,99 @@ import { estypes } from '@elastic/elasticsearch'; import { ValidFeatureId } from '@kbn/rule-data-utils'; -// TODO: validation (either via io-ts or a simple one) -// TODO: add JSDoc comments +/** + * Options that a plugin/solution provides to rule_registry in order to + * define and initialize an index for alerts-as-data. + * + * IMPORTANT: All names provided in these options are relative. For example: + * - component template refs will be 'ecs-mappings', not '.alerts-ecs-mappings' + * - component template names will be 'mappings', not '.alerts-security.alerts-mappings' + * - etc + */ export interface IndexOptions { + /** + * ID of the Kibana feature associated with the index. + * Used by alerts-as-data RBAC. + * + * Note from @dhurley14 + * The purpose of the `feature` param is to force the user to update + * the data structure which contains the mapping of consumers to alerts + * as data indices. The idea is it is typed such that it forces the + * user to go to the code and modify it. At least until a better system + * is put in place or we move the alerts as data client out of rule registry. + * + * @example 'siem', 'logs', 'apm' + */ feature: ValidFeatureId; + + /** + * Registration context which defines a solution or an app within a solution. + * @example 'security', 'observability', 'observability.logs' + */ registrationContext: string; + + /** + * Dataset suffix. Restricted to a few values. + * @example 'alerts', 'events' + */ dataset: Dataset; + + /** + * A list of references to external component templates. Those can be + * the common ones shared between all solutions, or special ones + * shared between some of them. + * + * IMPORTANT: These names should be relative. + * - correct: 'my-mappings' + * - incorrect: '.alerts-my-mappings' + * + * @example ['ecs-mappings'] + */ componentTemplateRefs: string[]; - componentTemplates: ComponentTemplateOptions[]; // NOTE: order matters + + /** + * Own component templates specified for the index by the plugin/solution + * defining this index. + * + * IMPORTANT: Order matters. This order is used by Elasticsearch to set + * priorities when merging the same field names defined in 2+ templates. + * + * IMPORTANT: Component template names should be relative. + * - correct: 'mappings' + * - incorrect: 'security.alerts-mappings' + * - incorrect: '.alerts-security.alerts-mappings' + */ + componentTemplates: ComponentTemplateOptions[]; + + /** + * Additional properties for the namespaced index template. + */ indexTemplate: IndexTemplateOptions; + + /** + * Optional custom ILM policy for the index. + * NOTE: this policy will be shared between all namespaces of the index. + */ ilmPolicy?: IlmPolicyOptions; + + /** + * Optional secondary alias that will be applied to concrete indices in + * addition to the primary one '.alerts-{reg. context}.{dataset}-{namespace}' + * + * IMPORTANT: It should not include the namespace. It will be added + * automatically. + * - correct: '.siem-signals' + * - incorrect: '.siem-signals-default' + * + * @example '.siem-signals', undefined + */ secondaryAlias?: string; } +/** + * Dataset suffix restricted to a few values. All alerts-as-data indices + * are designed to contain only documents of these "kinds". + */ export enum Dataset { alerts = 'alerts', events = 'events', @@ -31,6 +111,13 @@ export type Mappings = estypes.MappingTypeMapping; export type Version = estypes.VersionNumber; export type Meta = estypes.Metadata; +/** + * When initializing an index, a plugin/solution can break mappings and settings + * down into several component templates. Some of their properties can be + * defined by the plugin/solution via these options. + * + * https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-component-template.html + */ export interface ComponentTemplateOptions { name: string; version: Version; // TODO: encapsulate versioning (base on Kibana version) @@ -39,9 +126,28 @@ export interface ComponentTemplateOptions { _meta?: Meta; } +/** + * When initializing an index, a plugin/solution can provide some optional + * properties which will be included into the index template. + * + * Note that: + * - each index namespace will get its own index template + * - the template will be created by the library + * - most of its properties will be set by the library + * - you can inject some of them via these options + * + * https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-template.html + * https://www.elastic.co/guide/en/elasticsearch/reference/current/index-templates.html + */ export interface IndexTemplateOptions { version: Version; // TODO: encapsulate versioning (base on Kibana version) _meta?: Meta; } +/** + * When initializing an index, a plugin/solution can provide a custom + * ILM policy that will be applied to concrete indices of this index. + * + * Note that policy will be shared between all namespaces of the index. + */ export type IlmPolicyOptions = Omit; diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts index 764b8dc1bce68a4..585a42f8cc65353 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { get, isEmpty, once } from 'lodash'; +import { get, isEmpty } from 'lodash'; import { estypes } from '@elastic/elasticsearch'; import { ElasticsearchClient, Logger } from 'kibana/server'; @@ -19,21 +19,13 @@ import { technicalComponentTemplate } from '../../common/assets/component_templa import { ecsComponentTemplate } from '../../common/assets/component_templates/ecs_component_template'; import { defaultLifecyclePolicy } from '../../common/assets/lifecycle_policies/default_lifecycle_policy'; -import { ResourceNames } from './resource_names'; -import { IndexNames } from './index_names'; -import { IndexOptions } from './index_options'; +import { IndexInfo } from './index_info'; import { incrementIndexName } from './utils'; -export enum Resources { - common = 'common resources shared between all indices', - forIndex = 'resources for a particular index', - forNamespace = 'resources for a particular namespace', -} - -const INSTALLATION_TIMEOUT = 60000; +const INSTALLATION_TIMEOUT = 20 * 60 * 1000; // 20 minutes interface ConstructorOptions { - resourceNames: ResourceNames; + getResourceName(relativeName: string): string; getClusterClient: () => Promise; logger: Logger; isWriteEnabled: boolean; @@ -42,130 +34,132 @@ interface ConstructorOptions { export class ResourceInstaller { constructor(private readonly options: ConstructorOptions) {} - private getResourceName(...relativeNameSegments: string[]) { - return this.options.resourceNames.getFullName(...relativeNameSegments); - } + private async installWithTimeout( + resources: string, + installer: () => Promise + ): Promise { + try { + const installResources = async (): Promise => { + const { logger, isWriteEnabled } = this.options; - public memoizeInstallation( - resources: Resources, - installResources: () => Promise - ): () => Promise { - return once(async () => { - try { - return await Promise.race([ - installResources(), - new Promise((resolve, reject) => { - setTimeout(() => { - const msg = `Timeout: it took more than ${INSTALLATION_TIMEOUT}ms`; - reject(new Error(msg)); - }, INSTALLATION_TIMEOUT); - }), - ]); - } catch (e) { - this.options.logger.error(e); + if (!isWriteEnabled) { + logger.info(`Write is disabled; not installing ${resources}`); + return; + } - const reason = e?.message || 'Unknown reason'; - throw new Error(`Failure installing ${resources}. ${reason}`); - } - }); - } + logger.info(`Installing ${resources}`); + await installer(); + logger.info(`Installed ${resources}`); + }; + + const throwTimeoutException = (): Promise => { + return new Promise((resolve, reject) => { + setTimeout(() => { + const msg = `Timeout: it took more than ${INSTALLATION_TIMEOUT}ms`; + reject(new Error(msg)); + }, INSTALLATION_TIMEOUT); + }); + }; - public async installResourcesSharedBetweenAllIndices(): Promise { - const { logger, isWriteEnabled } = this.options; + await Promise.race([installResources(), throwTimeoutException()]); + } catch (e) { + this.options.logger.error(e); - if (!isWriteEnabled) { - logger.info(`Write is disabled; not installing ${Resources.common}`); - return; + const reason = e?.message || 'Unknown reason'; + throw new Error(`Failure installing ${resources}. ${reason}`); } + } - logger.info(`Installing ${Resources.common}`); - - await this.createOrUpdateLifecyclePolicy({ - policy: this.getResourceName(DEFAULT_ILM_POLICY_ID), - body: defaultLifecyclePolicy, - }); - - await this.createOrUpdateComponentTemplate({ - name: this.getResourceName(TECHNICAL_COMPONENT_TEMPLATE_NAME), - body: technicalComponentTemplate, - }); - - await this.createOrUpdateComponentTemplate({ - name: this.getResourceName(ECS_COMPONENT_TEMPLATE_NAME), - body: ecsComponentTemplate, + // ----------------------------------------------------------------------------------------------- + // Common resources + + /** + * Installs common, library-level resources shared between all indices: + * - default ILM policy + * - component template containing technical fields + * - component template containing all standard ECS fields + */ + public async installCommonResources(): Promise { + this.installWithTimeout('common resources shared between all indices', async () => { + const { getResourceName } = this.options; + + // We can install them in parallel + await Promise.all([ + this.createOrUpdateLifecyclePolicy({ + policy: getResourceName(DEFAULT_ILM_POLICY_ID), + body: defaultLifecyclePolicy, + }), + + this.createOrUpdateComponentTemplate({ + name: getResourceName(TECHNICAL_COMPONENT_TEMPLATE_NAME), + body: technicalComponentTemplate, + }), + + this.createOrUpdateComponentTemplate({ + name: getResourceName(ECS_COMPONENT_TEMPLATE_NAME), + body: ecsComponentTemplate, + }), + ]); }); - - logger.info(`Installed ${Resources.common}`); } - public async installResourcesSharedBetweenIndexNamespaces( - indexOptions: IndexOptions, - indexNames: IndexNames - ): Promise { - const { logger, isWriteEnabled } = this.options; - const { componentTemplates, ilmPolicy } = indexOptions; - - if (!isWriteEnabled) { - logger.info(`Write is disabled; not installing ${Resources.forIndex}`); - return; - } - - logger.info(`Installing ${Resources.forIndex}`); - - if (ilmPolicy != null) { - await this.createOrUpdateLifecyclePolicy({ - policy: indexNames.getIlmPolicyName(), - body: { policy: ilmPolicy }, - }); - } - - await Promise.all( - componentTemplates.map(async (ct) => { - await this.createOrUpdateComponentTemplate({ - name: indexNames.getComponentTemplateName(ct.name), - body: { - // TODO: difference? - // template: { - // settings: ct.settings, - // mappings: ct.mappings, - // version: ct.version, - // _meta: ct._meta, - // }, - template: { settings: {} }, - settings: ct.settings, - mappings: ct.mappings, - version: ct.version, - _meta: ct._meta, - }, + // ----------------------------------------------------------------------------------------------- + // Index-level resources + + /** + * Installs index-level resources shared between all namespaces of this index: + * - custom ILM policy if it was provided + * - component templates + * - attempts to update mappings of existing concrete indices + */ + public async installIndexLevelResources(indexInfo: IndexInfo): Promise { + this.installWithTimeout(`resources for index ${indexInfo.baseName}`, async () => { + const { componentTemplates, ilmPolicy } = indexInfo.indexOptions; + + if (ilmPolicy != null) { + await this.createOrUpdateLifecyclePolicy({ + policy: indexInfo.getIlmPolicyName(), + body: { policy: ilmPolicy }, }); - }) - ); + } - // TODO: Update all existing namespaced index templates matching this index' base name + await Promise.all( + componentTemplates.map(async (ct) => { + await this.createOrUpdateComponentTemplate({ + name: indexInfo.getComponentTemplateName(ct.name), + body: { + template: { + settings: ct.settings ?? {}, + mappings: ct.mappings, + }, + version: ct.version, + _meta: ct._meta, + }, + }); + }) + ); - await this.updateIndexMappings(indexNames); + // TODO: Update all existing namespaced index templates matching this index' base name - logger.info(`Installed ${Resources.forIndex}`); + await this.updateIndexMappings(indexInfo); + }); } - private async updateIndexMappings(indexNames: IndexNames) { + private async updateIndexMappings(indexInfo: IndexInfo) { const { logger, getClusterClient } = this.options; const clusterClient = await getClusterClient(); - logger.debug(`Updating mappings of existing indices`); + logger.debug(`Updating mappings of existing concrete indices for ${indexInfo.baseName}`); const { body: aliasesResponse } = await clusterClient.indices.getAlias({ - index: indexNames.basePattern, + index: indexInfo.basePattern, }); - const writeIndicesAndAliases: Array<{ index: string; alias: string }> = []; - Object.entries(aliasesResponse).forEach(([index, aliases]) => { - Object.entries(aliases.aliases).forEach(([aliasName, aliasProperties]) => { - if (aliasProperties.is_write_index) { - writeIndicesAndAliases.push({ index, alias: aliasName }); - } - }); - }); + const writeIndicesAndAliases = Object.entries(aliasesResponse).flatMap(([index, { aliases }]) => + Object.entries(aliases) + .filter(([, aliasProperties]) => aliasProperties.is_write_index) + .map(([aliasName]) => ({ index, alias: aliasName })) + ); await Promise.all( writeIndicesAndAliases.map((indexAndAlias) => @@ -222,17 +216,28 @@ export class ResourceInstaller { } } - public async createWriteTargetIfNeeded( - indexOptions: IndexOptions, - indexNames: IndexNames, + // ----------------------------------------------------------------------------------------------- + // Namespace-level resources + + /** + * Installs resources tied to concrete namespace of an index: + * - namespaced index template + * - concrete index (write target) if it doesn't exist + */ + public async installNamespaceLevelResources( + indexInfo: IndexInfo, namespace: string - ) { + ): Promise { + await this.createWriteTargetIfNeeded(indexInfo, namespace); + } + + private async createWriteTargetIfNeeded(indexInfo: IndexInfo, namespace: string) { const { logger, getClusterClient } = this.options; const clusterClient = await getClusterClient(); - const primaryNamespacedAlias = indexNames.getPrimaryAlias(namespace); - const primaryNamespacedPattern = indexNames.getPrimaryAliasPattern(namespace); - const initialIndexName = indexNames.getConcreteIndexInitialName(namespace); + const primaryNamespacedAlias = indexInfo.getPrimaryAlias(namespace); + const primaryNamespacedPattern = indexInfo.getPrimaryAliasPattern(namespace); + const initialIndexName = indexInfo.getConcreteIndexInitialName(namespace); logger.debug(`Creating write target for ${primaryNamespacedAlias}`); @@ -242,7 +247,7 @@ export class ResourceInstaller { }); if (!indicesExist) { - await this.installNamespacedIndexTemplate(indexOptions, indexNames, namespace); + await this.installNamespacedIndexTemplate(indexInfo, namespace); try { await clusterClient.indices.create({ @@ -292,29 +297,29 @@ export class ResourceInstaller { } } - private async installNamespacedIndexTemplate( - indexOptions: IndexOptions, - indexNames: IndexNames, - namespace: string - ) { - const { logger } = this.options; + private async installNamespacedIndexTemplate(indexInfo: IndexInfo, namespace: string) { + const { logger, getResourceName } = this.options; + const { + componentTemplateRefs, + componentTemplates, + indexTemplate, + ilmPolicy, + } = indexInfo.indexOptions; - const primaryNamespacedAlias = indexNames.getPrimaryAlias(namespace); - const primaryNamespacedPattern = indexNames.getPrimaryAliasPattern(namespace); - const secondaryNamespacedAlias = indexNames.getSecondaryAlias(namespace); + const primaryNamespacedAlias = indexInfo.getPrimaryAlias(namespace); + const primaryNamespacedPattern = indexInfo.getPrimaryAliasPattern(namespace); + const secondaryNamespacedAlias = indexInfo.getSecondaryAlias(namespace); logger.debug(`Installing index template for ${primaryNamespacedAlias}`); - const technicalComponentNames = [this.getResourceName(TECHNICAL_COMPONENT_TEMPLATE_NAME)]; - const referencedComponentNames = indexOptions.componentTemplateRefs.map((ref) => - this.getResourceName(ref) - ); - const ownComponentNames = indexOptions.componentTemplates.map((template) => - indexNames.getComponentTemplateName(template.name) + const technicalComponentNames = [getResourceName(TECHNICAL_COMPONENT_TEMPLATE_NAME)]; + const referencedComponentNames = componentTemplateRefs.map((ref) => getResourceName(ref)); + const ownComponentNames = componentTemplates.map((template) => + indexInfo.getComponentTemplateName(template.name) ); - const ilmPolicyName = indexOptions.ilmPolicy - ? indexNames.getIlmPolicyName() - : this.getResourceName(DEFAULT_ILM_POLICY_ID); + const ilmPolicyName = ilmPolicy + ? indexInfo.getIlmPolicyName() + : getResourceName(DEFAULT_ILM_POLICY_ID); // TODO: need a way to update this template if/when we decide to make changes to the // built in index template. Probably do it as part of updateIndexMappingsForAsset? @@ -327,7 +332,7 @@ export class ResourceInstaller { // the namespace values can really only come from the existing templates that we're trying to update // - maybe we want to store the namespace as a _meta field on the index template for easy retrieval await this.createOrUpdateIndexTemplate({ - name: indexNames.getIndexTemplateName(namespace), + name: indexInfo.getIndexTemplateName(namespace), body: { index_patterns: [primaryNamespacedPattern], @@ -335,7 +340,7 @@ export class ResourceInstaller { // - first go external component templates referenced by this index (e.g. the common full ECS template) // - then we include own component templates registered with this index // - finally, we include technical component templates to make sure the index gets all the - // mappings and settings required by all Kibana plugins using rule_registry to work properly + // mappings and settings required by all Kibana plugins using rule registry to work properly composed_of: [ ...referencedComponentNames, ...ownComponentNames, @@ -362,11 +367,11 @@ export class ResourceInstaller { }, _meta: { - ...indexOptions.indexTemplate._meta, + ...indexTemplate._meta, namespace, }, - version: indexOptions.indexTemplate.version, + version: indexTemplate.version, // By setting the priority to namespace.length, we ensure that if one namespace is a prefix of another namespace // then newly created indices will use the matching template with the *longest* namespace diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_names.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_names.ts deleted file mode 100644 index d07cb2ba47e1028..000000000000000 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_names.ts +++ /dev/null @@ -1,29 +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. - */ - -const joinWithDash = (...names: string[]): string => names.filter(Boolean).join('-'); - -interface ConstructorOptions { - /** @example '.alerts' */ - indexPrefixFromConfig: string; -} - -export class ResourceNames { - public static joinWithDash = joinWithDash; - - constructor(private readonly options: ConstructorOptions) {} - - /** @example '.alerts' */ - public getFullPrefix(): string { - // TODO: https://github.com/elastic/kibana/issues/106432 - return this.options.indexPrefixFromConfig; - } - - public getFullName(...relativeNameSegments: string[]) { - return joinWithDash(this.getFullPrefix(), ...relativeNameSegments); - } -} diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts index 5b6cf0bef0fed47..467d6816d044331 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts @@ -6,7 +6,7 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; -import { RuleDataPluginService } from './'; +import { RuleDataPluginService } from './rule_data_plugin_service'; type Schema = PublicMethodsOf; diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts index abb7b04c0ce8fea..948ffd23b252fef 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts @@ -7,11 +7,11 @@ import { ElasticsearchClient, Logger } from 'kibana/server'; -import { RuleDataClient } from '../rule_data_client'; -import { IndexNames } from './index_names'; +import { IRuleDataClient, RuleDataClient } from '../rule_data_client'; +import { IndexInfo } from './index_info'; import { IndexOptions } from './index_options'; -import { ResourceNames } from './resource_names'; -import { ResourceInstaller, Resources } from './resource_installer'; +import { ResourceInstaller } from './resource_installer'; +import { joinWithDash } from './utils'; interface ConstructorOptions { getClusterClient: () => Promise; @@ -20,77 +20,107 @@ interface ConstructorOptions { index: string; } +/** + * A service for creating and using Elasticsearch indices for alerts-as-data. + */ export class RuleDataPluginService { - private readonly resourceNames: ResourceNames; private readonly resourceInstaller: ResourceInstaller; - private readonly installCommonResources: () => Promise; + private installCommonResources: Promise; + private isInitialized: boolean; constructor(private readonly options: ConstructorOptions) { - this.resourceNames = new ResourceNames({ indexPrefixFromConfig: options.index }); this.resourceInstaller = new ResourceInstaller({ - resourceNames: this.resourceNames, + getResourceName: (name) => this.getResourceName(name), getClusterClient: options.getClusterClient, logger: options.logger, isWriteEnabled: options.isWriteEnabled, }); - this.installCommonResources = this.resourceInstaller.memoizeInstallation(Resources.common, () => - this.resourceInstaller.installResourcesSharedBetweenAllIndices() - ); + this.installCommonResources = Promise.resolve(); + this.isInitialized = false; } - public getResourcePrefix() { - return this.resourceNames.getFullPrefix(); + /** + * Returns a full resource prefix. + * - it's '.alerts' by default + * - it can be adjusted by the user via Kibana config + */ + public getResourcePrefix(): string { + // TODO: https://github.com/elastic/kibana/issues/106432 + return this.options.index; } - public getResourceName(...relativeNameSegments: string[]) { - return this.resourceNames.getFullName(...relativeNameSegments); + /** + * Prepends a relative resource name with a full resource prefix, which + * starts with '.alerts' and can optionally include a user-defined part in it. + * @returns Full name of the resource. + * @example 'security.alerts' => '.alerts-security.alerts' + */ + public getResourceName(relativeName: string): string { + return joinWithDash(this.getResourcePrefix(), relativeName); } + /** + * If write is enabled, everything works as usual. + * If it's disabled, writing to all alerts-as-data indices will be disabled, + * and also Elasticsearch resources associated with the indices will not be + * installed. + */ public isWriteEnabled(): boolean { return this.options.isWriteEnabled; } + /** + * Installs common Elasticsearch resources used by all alerts-as-data indices. + */ public initializeService(): void { - this.installCommonResources().catch((e) => { + // Run the installation of common resources and handle errors. + this.installCommonResources = this.resourceInstaller.installCommonResources().catch((e) => { this.options.logger.error(e); + throw e; // re-throws to propagate it to the index initialization phase }); + this.isInitialized = true; } - public initializeIndex(indexOptions: IndexOptions): RuleDataClient { - const { feature, registrationContext, dataset, secondaryAlias } = indexOptions; - - const indexNames = new IndexNames({ - resourceNames: this.resourceNames, - registrationContext, - dataset, - secondaryAlias: secondaryAlias ?? null, + /** + * Initializes alerts-as-data index and starts index bootstrapping right away. + * @param indexOptions Index parameters: names and resources. + * @returns Client for reading and writing data to this index. + */ + public initializeIndex(indexOptions: IndexOptions): IRuleDataClient { + if (!this.isInitialized) { + throw new Error( + 'Rule data service is not initialized. Make sure to call initializeService() in the rule registry plugin setup phase' + ); + } + + const indexInfo = new IndexInfo({ + getResourceName: (name) => this.getResourceName(name), + indexOptions, }); - const installIndexResources = this.resourceInstaller.memoizeInstallation( - Resources.forIndex, - async () => { - await this.installCommonResources(); - await this.resourceInstaller.installResourcesSharedBetweenIndexNamespaces( - indexOptions, - indexNames - ); - - const clusterClient = await this.options.getClusterClient(); - return { clusterClient }; - } - ); + const installIndexResources = async () => { + await this.installCommonResources; + await this.resourceInstaller.installIndexLevelResources(indexInfo); + return this.options.getClusterClient(); + }; - // Start installation eagerly - const installPromise = installIndexResources(); + // Start installation right away + const waitUntilIndexResourcesInstalled = installIndexResources().catch((e) => { + this.options.logger.error(e); + return (null as unknown) as ElasticsearchClient; + }); + const waitUntilClusterClientAvailable = this.options.getClusterClient(); return new RuleDataClient({ - feature, - indexNames, - indexOptions, + indexInfo, resourceInstaller: this.resourceInstaller, isWriteEnabled: this.isWriteEnabled(), - waitUntilIndexIsReady: () => installPromise, + + // Let's unblock read operations since installation can take quite some time. + // Write operations will have to wait, of course. + waitUntilReadyForReading: waitUntilClusterClientAvailable, + waitUntilReadyForWriting: waitUntilIndexResourcesInstalled, }); } } diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/utils.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/utils.ts index aaab338ff858a3a..10f2f546046b7e4 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/utils.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/utils.ts @@ -5,11 +5,19 @@ * 2.0. */ -export function incrementIndexName(oldIndex: string) { +export const incrementIndexName = (oldIndex: string) => { const baseIndexString = oldIndex.slice(0, -6); const newIndexNumber = Number(oldIndex.slice(-6)) + 1; if (isNaN(newIndexNumber)) { return undefined; } return baseIndexString + String(newIndexNumber).padStart(6, '0'); -} +}; + +export const joinWith = (separator: string) => ( + ...items: Array +): string => { + return items.filter(Boolean).map(String).join(separator); +}; + +export const joinWithDash = joinWith('-'); diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index 7a00457f2c4e12d..e6db167d8c6b654 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -35,7 +35,7 @@ import { TIMESTAMP, SPACE_IDS, } from '../../common/technical_rule_data_field_names'; -import { RuleDataClient } from '../rule_data_client'; +import { IRuleDataClient } from '../rule_data_client'; import { AlertExecutorOptionsWithExtraServices } from '../types'; import { getRuleData } from './get_rule_executor_data'; @@ -102,7 +102,7 @@ export type WrappedLifecycleRuleState = AlertTypeS export const createLifecycleExecutor = ( logger: Logger, - ruleDataClient: PublicContract + ruleDataClient: PublicContract ) => < Params extends AlertTypeParams = never, State extends AlertTypeState = never, diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts index 21e95fbefe4e2bd..9a809efdb5789f4 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type_factory.ts @@ -5,7 +5,7 @@ * 2.0. */ import { Logger } from '@kbn/logging'; -import { RuleDataClient } from '..'; +import { IRuleDataClient } from '../rule_data_client'; import { AlertInstanceContext, AlertInstanceState, @@ -20,7 +20,7 @@ export const createLifecycleRuleTypeFactory = ({ ruleDataClient, }: { logger: Logger; - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; }) => < TParams extends AlertTypeParams, TAlertInstanceContext extends AlertInstanceContext, diff --git a/x-pack/plugins/rule_registry/server/utils/persistence_types.ts b/x-pack/plugins/rule_registry/server/utils/persistence_types.ts index ea84001dbffd61c..11607909a2e0f88 100644 --- a/x-pack/plugins/rule_registry/server/utils/persistence_types.ts +++ b/x-pack/plugins/rule_registry/server/utils/persistence_types.ts @@ -15,7 +15,7 @@ import { AlertTypeParams, AlertTypeState, } from '../../../alerting/server'; -import { RuleDataClient } from '../rule_data_client'; +import { IRuleDataClient } from '../rule_data_client'; import { AlertTypeWithExecutor } from '../types'; export type PersistenceAlertService< @@ -38,7 +38,7 @@ export interface PersistenceServices < TState extends AlertTypeState, diff --git a/x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts b/x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts index 7943c3ad4f35acb..0bee5bdf53a695a 100644 --- a/x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts +++ b/x-pack/plugins/rule_registry/server/utils/with_rule_data_client_factory.ts @@ -6,10 +6,10 @@ */ import { AlertInstanceContext, AlertTypeParams, AlertTypeState } from '../../../alerting/common'; -import { RuleDataClient } from '../rule_data_client'; +import { IRuleDataClient } from '../rule_data_client'; import { AlertTypeWithExecutor } from '../types'; -export const withRuleDataClientFactory = (ruleDataClient: RuleDataClient) => < +export const withRuleDataClientFactory = (ruleDataClient: IRuleDataClient) => < TState extends AlertTypeState, TParams extends AlertTypeParams, TAlertInstanceContext extends AlertInstanceContext, @@ -19,13 +19,13 @@ export const withRuleDataClientFactory = (ruleDataClient: RuleDataClient) => < TState, TParams, TAlertInstanceContext, - TServices & { ruleDataClient: RuleDataClient } + TServices & { ruleDataClient: IRuleDataClient } > ): AlertTypeWithExecutor< TState, TParams, TAlertInstanceContext, - TServices & { ruleDataClient: RuleDataClient } + TServices & { ruleDataClient: IRuleDataClient } > => { return { ...type, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index 8b5c4afbd5e8d68..7dad03ed7e14e3f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -110,7 +110,7 @@ export const createDetectionIndex = async ( if (!policyExists) { await setPolicy(esClient, index, signalsPolicy); } - const aadIndexAliasName = ruleDataService.getResourceName('security.alerts', spaceId); + const aadIndexAliasName = ruleDataService.getResourceName(`security.alerts-${spaceId}`); if (await templateNeedsUpdate({ alias: index, esClient })) { await esClient.indices.putIndexTemplate({ name: index, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index 6ada1e705a85268..b7f32b82cc767b1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -6,7 +6,7 @@ */ import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils'; -import { RuleDataClient } from '../../../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../../../rule_registry/server'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; @@ -25,7 +25,7 @@ import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters' export const createRulesRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - ruleDataClient?: RuleDataClient | null // TODO: Use this for RAC (otherwise delete it) + ruleDataClient?: IRuleDataClient | null // TODO: Use this for RAC (otherwise delete it) ): void => { router.post( { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts index 73d541802f055f5..2cee8301a05ffb2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -6,7 +6,7 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; -import { RuleDataClient } from '../../../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../../../rule_registry/server'; import { queryRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/query_rules_type_dependents'; import { queryRulesSchema, @@ -23,7 +23,7 @@ import { readRules } from '../../rules/read_rules'; export const deleteRulesRoute = ( router: SecuritySolutionPluginRouter, - ruleDataClient?: RuleDataClient | null + ruleDataClient?: IRuleDataClient | null ) => { router.delete( { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts index f0483c935f71c04..4a464c19f5b9758 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts @@ -6,7 +6,7 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; -import { RuleDataClient } from '../../../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../../../rule_registry/server'; import { findRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/find_rules_type_dependents'; import { findRulesSchema, @@ -22,7 +22,7 @@ import { getBulkRuleActionsSavedObject } from '../../rule_actions/get_bulk_rule_ export const findRulesRoute = ( router: SecuritySolutionPluginRouter, - ruleDataClient?: RuleDataClient | null + ruleDataClient?: IRuleDataClient | null ) => { router.get( { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts index 45217fbd5e62ce3..1efc9c93b08d2e6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -6,7 +6,7 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; -import { RuleDataClient } from '../../../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../../../rule_registry/server'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { patchRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/patch_rules_type_dependents'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; @@ -31,7 +31,7 @@ import { PartialFilter } from '../../types'; export const patchRulesRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - ruleDataClient?: RuleDataClient | null + ruleDataClient?: IRuleDataClient | null ) => { router.patch( { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts index fc290190d86ee1b..6d5e63b2a058825 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts @@ -6,7 +6,7 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; -import { RuleDataClient } from '../../../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../../../rule_registry/server'; import { queryRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/query_rules_type_dependents'; import { queryRulesSchema, @@ -24,7 +24,7 @@ import { RuleExecutionStatus } from '../../../../../common/detection_engine/sche export const readRulesRoute = ( router: SecuritySolutionPluginRouter, - ruleDataClient?: RuleDataClient | null + ruleDataClient?: IRuleDataClient | null ) => { router.get( { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts index 23449227f6c7092..368b02fdb1e9490 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -6,7 +6,7 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; -import { RuleDataClient } from '../../../../../../rule_registry/server'; +import { IRuleDataClient } from '../../../../../../rule_registry/server'; import { updateRulesSchema } from '../../../../../common/detection_engine/schemas/request'; import { updateRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/update_rules_type_dependents'; import type { SecuritySolutionPluginRouter } from '../../../../types'; @@ -25,7 +25,7 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v export const updateRulesRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - ruleDataClient?: RuleDataClient | null + ruleDataClient?: IRuleDataClient | null ) => { router.put( { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts index 024ea1c22cfb04d..3f2f34c17679f05 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts @@ -18,7 +18,7 @@ import { import moment from 'moment'; import { mappingFromFieldMap } from '../../../../../../rule_registry/common/mapping_from_field_map'; -import { Dataset, RuleDataClient } from '../../../../../../rule_registry/server'; +import { Dataset, IRuleDataClient } from '../../../../../../rule_registry/server'; import { SERVER_APP_ID } from '../../../../../common/constants'; import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; import { invariant } from '../../../../../common/utils/invariant'; @@ -71,7 +71,7 @@ interface IRuleRegistryLogClient { */ export class RuleRegistryLogClient implements IRuleRegistryLogClient { private sequence = 0; - private ruleDataClient: RuleDataClient; + private ruleDataClient: IRuleDataClient; constructor(ruleDataService: IRuleDataPluginService) { this.ruleDataClient = ruleDataService.initializeIndex({ @@ -83,9 +83,6 @@ export class RuleRegistryLogClient implements IRuleRegistryLogClient { { name: 'mappings', version: 0, - settings: { - number_of_shards: 1, - }, mappings: mappingFromFieldMap(ruleExecutionFieldMap, 'strict'), }, ], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index 151b1d8b2fb13f1..3c28551a71debb1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -11,7 +11,7 @@ import { v4 } from 'uuid'; import { Logger, SavedObject } from 'kibana/server'; import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; -import type { RuleDataClient } from '../../../../../../rule_registry/server'; +import type { IRuleDataClient } from '../../../../../../rule_registry/server'; import { PluginSetupContract as AlertingPluginSetupContract } from '../../../../../../alerting/server'; import { ConfigType } from '../../../../config'; import { AlertAttributes } from '../../signals/types'; @@ -89,7 +89,8 @@ export const createRuleTypeMocks = ( bulk: jest.fn(), })), isWriteEnabled: jest.fn(() => true), - } as unknown) as RuleDataClient, + indexName: '.alerts-security.alerts', + } as unknown) as IRuleDataClient, ruleDataService: ruleRegistryMocks.createRuleDataPluginService(), }, services, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts index 50aff0019139652..4a9d1b56583172d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts @@ -61,7 +61,6 @@ describe('Indicator Match Alerts', () => { const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); const indicatorMatchAlertType = createIndicatorMatchAlertType({ experimentalFeatures: allowedExperimentalValues, - indexAlias: 'alerts.security-alerts', lists: dependencies.lists, logger: dependencies.logger, mergeStrategy: 'allFields', @@ -102,7 +101,6 @@ describe('Indicator Match Alerts', () => { const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); const indicatorMatchAlertType = createIndicatorMatchAlertType({ experimentalFeatures: allowedExperimentalValues, - indexAlias: 'alerts.security-alerts', lists: dependencies.lists, logger: dependencies.logger, mergeStrategy: 'allFields', @@ -141,7 +139,6 @@ describe('Indicator Match Alerts', () => { const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); const indicatorMatchAlertType = createIndicatorMatchAlertType({ experimentalFeatures: allowedExperimentalValues, - indexAlias: 'alerts.security-alerts', lists: dependencies.lists, logger: dependencies.logger, mergeStrategy: 'allFields', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts index 61342981479aede..71acc2e1cee8533 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -16,7 +16,6 @@ import { CreateRuleOptions } from '../types'; export const createIndicatorMatchAlertType = (createOptions: CreateRuleOptions) => { const { experimentalFeatures, - indexAlias, lists, logger, mergeStrategy, @@ -25,7 +24,6 @@ export const createIndicatorMatchAlertType = (createOptions: CreateRuleOptions) ruleDataService, } = createOptions; const createSecurityRuleType = createSecurityRuleTypeFactory({ - indexAlias, lists, logger, mergeStrategy, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 2d2f9dee8533a30..e781bfc50bee4b8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -23,7 +23,7 @@ import { TypeOfFieldMap } from '../../../../../rule_registry/common/field_map'; import { AlertTypeWithExecutor, PersistenceServices, - RuleDataClient, + IRuleDataClient, } from '../../../../../rule_registry/server'; import { BaseHit } from '../../../../common/detection_engine/types'; import { ConfigType } from '../../../config'; @@ -96,7 +96,7 @@ export type CreateSecurityRuleTypeFactory = (options: { lists: SetupPlugins['lists']; logger: Logger; mergeStrategy: ConfigType['alertMergeStrategy']; - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; ruleDataService: IRuleDataPluginService; }) => < TParams extends RuleParams & { index: string[] | undefined }, @@ -124,7 +124,7 @@ export interface CreateRuleOptions { lists: SetupPlugins['lists']; logger: Logger; mergeStrategy: ConfigType['alertMergeStrategy']; - ruleDataClient: RuleDataClient; + ruleDataClient: IRuleDataClient; version: string; ruleDataService: IRuleDataPluginService; } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 073f6cefcd9754e..221d65e9e45f548 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -32,7 +32,7 @@ import { PluginStartContract as CasesPluginStartContract } from '../../cases/ser import { ECS_COMPONENT_TEMPLATE_NAME } from '../../rule_registry/common/assets'; import { SecurityPluginSetup as SecuritySetup, SecurityPluginStart } from '../../security/server'; import { - RuleDataClient, + IRuleDataClient, RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract, Dataset, @@ -204,7 +204,7 @@ export class Plugin implements IPlugin { // Detection Engine Rule routes that have the REST endpoints of /api/detection_engine/rules // All REST rule creation, deletion, updating, etc...... diff --git a/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts index 8bbf20f3a64ad9f..1927162f6477233 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts @@ -9,7 +9,7 @@ import { Logger } from 'kibana/server'; import { UMServerLibs } from '../../lib'; import { UptimeCorePlugins, UptimeCoreSetup } from '../../adapters'; import type { UptimeRouter } from '../../../types'; -import type { RuleDataClient } from '../../../../../rule_registry/server'; +import type { IRuleDataClient } from '../../../../../rule_registry/server'; import { getUptimeESMockClient } from '../../requests/helper'; import { alertsMock } from '../../../../../alerting/server/mocks'; import { DynamicSettings } from '../../../../common/runtime_types'; @@ -72,7 +72,9 @@ export const createRuleTypeMocks = ( bulk: jest.fn(), }; }, - } as unknown) as RuleDataClient, + isWriteEnabled: jest.fn(() => true), + indexName: '.alerts-observability.synthetics.alerts', + } as unknown) as IRuleDataClient, }, services, scheduleActions, diff --git a/x-pack/plugins/uptime/server/plugin.ts b/x-pack/plugins/uptime/server/plugin.ts index 8b4c0799ef21b53..3e935eab153acda 100644 --- a/x-pack/plugins/uptime/server/plugin.ts +++ b/x-pack/plugins/uptime/server/plugin.ts @@ -44,9 +44,6 @@ export class Plugin implements PluginType { { name: 'mappings', version: 0, - settings: { - number_of_shards: 1, - }, mappings: mappingFromFieldMap(uptimeRuleFieldMap, 'strict'), }, ], diff --git a/x-pack/plugins/uptime/server/uptime_server.ts b/x-pack/plugins/uptime/server/uptime_server.ts index f52b4a806335139..ded76027a3c3a63 100644 --- a/x-pack/plugins/uptime/server/uptime_server.ts +++ b/x-pack/plugins/uptime/server/uptime_server.ts @@ -6,7 +6,7 @@ */ import { Logger } from 'kibana/server'; -import { createLifecycleRuleTypeFactory, RuleDataClient } from '../../rule_registry/server'; +import { createLifecycleRuleTypeFactory, IRuleDataClient } from '../../rule_registry/server'; import { UMServerLibs } from './lib/lib'; import { createRouteWithAuth, restApiRoutes, uptimeRouteWrapper } from './rest_api'; import { UptimeCoreSetup, UptimeCorePlugins } from './lib/adapters'; @@ -20,7 +20,7 @@ export const initUptimeServer = ( server: UptimeCoreSetup, libs: UMServerLibs, plugins: UptimeCorePlugins, - ruleDataClient: RuleDataClient, + ruleDataClient: IRuleDataClient, logger: Logger ) => { restApiRoutes.forEach((route) => diff --git a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts index 00385e4ff302891..8c59486600295f6 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.ts @@ -42,7 +42,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { const BULK_INDEX_DELAY = 1000; const INDEXING_DELAY = 5000; - const ALERTS_INDEX_TARGET = '.kibana-alerts-*-apm*'; + // const ALERTS_INDEX_TARGET = '.kibana-alerts-*-apm*'; + const ALERTS_INDEX_TARGET = '.kibana-alerts-observability.apm.alerts*'; const APM_TRANSACTION_INDEX_NAME = 'apm-8.0.0-transaction'; const createTransactionEvent = (override: Record) => {