From 7ca6237077a4a42944260d5e6b7f7e5a8e6e496e Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Mon, 16 Dec 2024 09:16:43 +0100 Subject: [PATCH 1/3] [ES Query] Fix saving ECS group by fields for query DSL rule (#203769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #203472 ## Summary |Rule|Group info| |---|---| |![image](https://github.com/user-attachments/assets/fc17c630-d7c2-4615-8056-5e04209b71e6)|![image](https://github.com/user-attachments/assets/55328973-d585-4148-a74f-d2c275b9989d)| @elastic/response-ops What sort of test do you suggest to add for this case? ### 🧪 How to run test #### Deployment agnostic - [x] Test on MKI ``` // Server node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts // Test node scripts/functional_test_runner --config=x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="ElasticSearch query rule" ``` (cherry picked from commit a0fe4e698a031cb36b9dc0c2f8450561f9ea888e) # Conflicts: # x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl.ts # x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query_rule.ts # x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts # x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts --- .../rule_types/es_query/lib/fetch_es_query.ts | 1 + .../es_query/query_dsl_only.ts | 2 +- .../observability/alerting/es_query/index.ts | 14 ++ .../es_query/query_dsl_with_group_by.ts | 207 ++++++++++++++++++ .../observability/alerting/es_query/types.ts | 16 ++ .../apis/observability/alerting/index.ts | 3 +- .../es_query_rule/es_query_rule.ts | 125 ----------- 7 files changed, 241 insertions(+), 127 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts delete mode 100644 x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts index 1e6c9a498c0ad..1b29b197df136 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/lib/fetch_es_query.ts @@ -154,6 +154,7 @@ export async function fetchEsQuery({ esResult: searchResult, resultLimit: alertLimit, sourceFieldsParams: params.sourceFields, + termField: params.termField, }), link, query: sortedQuery, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts index 6fcdf9be2f40f..b25368b14ae74 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts @@ -33,7 +33,7 @@ export default function ruleTests({ getService }: FtrProviderContext) { const { es, esTestIndexTool, esTestIndexToolOutput, createEsDocumentsInGroups, waitForDocs } = getRuleServices(getService); - describe('rule', () => { + describe('Query DSL only', () => { let endDate: string; let connectorId: string; const objectRemover = new ObjectRemover(supertest); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts new file mode 100644 index 0000000000000..680184f7c468b --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('ElasticSearch query rule', () => { + loadTestFile(require.resolve('./query_dsl_with_group_by')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts new file mode 100644 index 0000000000000..db91ba5780dad --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts @@ -0,0 +1,207 @@ +/* + * 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 expect from '@kbn/expect'; +import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; +import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services'; +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ActionDocument } from './types'; + +export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { + const esClient = getService('es'); + const logger = getService('log'); + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const alertingApi = getService('alertingApi'); + const config = getService('config'); + const isServerless = config.get('serverless'); + const expectedConsumer = isServerless ? 'observability' : 'logs'; + + let adminRoleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; + + describe('Query DQL with group by field', () => { + const RULE_TYPE_ID = '.es-query'; + const ALERT_ACTION_INDEX = 'alert-action-es-query'; + const RULE_ALERT_INDEX = '.alerts-stack.alerts-default'; + const INDEX = 'kbn-data-forge-fake_hosts.fake_hosts-*'; + let dataForgeConfig: PartialConfig; + let dataForgeIndices: string[]; + let actionId: string; + let ruleId: string; + + before(async () => { + adminRoleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + internalReqHeader = samlAuth.getInternalRequestHeader(); + dataForgeConfig = { + schedule: [ + { + template: 'good', + start: 'now-10m', + end: 'now+5m', + metrics: [ + { name: 'system.cpu.user.pct', method: 'linear', start: 2.5, end: 2.5 }, + { name: 'system.cpu.total.pct', method: 'linear', start: 0.5, end: 0.5 }, + { name: 'system.cpu.total.norm.pct', method: 'linear', start: 0.8, end: 0.8 }, + ], + }, + ], + indexing: { + dataset: 'fake_hosts' as Dataset, + eventsPerCycle: 1, + interval: 60000, + alignEventsToInterval: true, + }, + }; + dataForgeIndices = await generate({ client: esClient, config: dataForgeConfig, logger }); + await alertingApi.waitForDocumentInIndex({ + indexName: dataForgeIndices.join(','), + docCountTarget: 45, + }); + }); + + after(async () => { + await supertestWithoutAuth + .delete(`/api/alerting/rule/${ruleId}`) + .set(adminRoleAuthc.apiKeyHeader) + .set(internalReqHeader); + await supertestWithoutAuth + .delete(`/api/actions/connector/${actionId}`) + .set(adminRoleAuthc.apiKeyHeader) + .set(internalReqHeader); + await esClient.deleteByQuery({ + index: RULE_ALERT_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + conflicts: 'proceed', + }); + await esClient.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'rule.id': ruleId } }, + conflicts: 'proceed', + }); + await esDeleteAllIndices([ALERT_ACTION_INDEX, ...dataForgeIndices]); + await cleanup({ client: esClient, config: dataForgeConfig, logger }); + await samlAuth.invalidateM2mApiKeyWithRoleScope(adminRoleAuthc); + }); + + describe('Rule creation', () => { + it('creates rule successfully', async () => { + actionId = await alertingApi.createIndexConnector({ + roleAuthc: adminRoleAuthc, + name: 'Index Connector: Alerting API test', + indexName: ALERT_ACTION_INDEX, + }); + + const createdRule = await alertingApi.helpers.createEsQueryRule({ + roleAuthc: adminRoleAuthc, + consumer: expectedConsumer, + name: 'always fire', + ruleTypeId: RULE_TYPE_ID, + params: { + size: 100, + thresholdComparator: '>', + threshold: [1], + index: [INDEX], + timeField: '@timestamp', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + timeWindowSize: 1, + timeWindowUnit: 'm', + groupBy: 'top', + termField: 'host.name', + termSize: 1, + }, + actions: [ + { + group: 'query matched', + id: actionId, + params: { + documents: [ + { + ruleId: '{{rule.id}}', + ruleName: '{{rule.name}}', + ruleParams: '{{rule.params}}', + spaceId: '{{rule.spaceId}}', + tags: '{{rule.tags}}', + alertId: '{{alert.id}}', + alertActionGroup: '{{alert.actionGroup}}', + instanceContextValue: '{{context.instanceContextValue}}', + instanceStateValue: '{{state.instanceStateValue}}', + }, + ], + }, + frequency: { + notify_when: 'onActiveAlert', + throttle: null, + summary: false, + }, + }, + ], + }); + ruleId = createdRule.id; + expect(ruleId).not.to.be(undefined); + }); + + it('should be active', async () => { + const executionStatus = await alertingApi.waitForRuleStatus({ + roleAuthc: adminRoleAuthc, + ruleId, + expectedStatus: 'active', + }); + expect(executionStatus).to.be('active'); + }); + + it('should find the created rule with correct information about the consumer', async () => { + const match = await alertingApi.findInRules(adminRoleAuthc, ruleId); + expect(match).not.to.be(undefined); + expect(match.consumer).to.be(expectedConsumer); + }); + + it('should set correct information in the alert document', async () => { + const resp = await alertingApi.waitForAlertInIndex({ + indexName: RULE_ALERT_INDEX, + ruleId, + }); + + expect(resp.hits.hits[0]._source).property('host.name', 'host-0'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', expectedConsumer); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.reason', + 'Document count is 3 in the last 1m for host-0 in kbn-data-forge-fake_hosts.fake_hosts-* index. Alert when greater than 1.' + ); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.evaluation.conditions', + 'Number of matching documents for group "host-0" is greater than 1' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.evaluation.value', '3'); + expect(resp.hits.hits[0]._source).property('kibana.alert.evaluation.threshold', 1); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'always fire'); + expect(resp.hits.hits[0]._source).property('event.action', 'open'); + expect(resp.hits.hits[0]._source).property('kibana.alert.action_group', 'query matched'); + expect(resp.hits.hits[0]._source).property('kibana.alert.status', 'active'); + expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open'); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.category', + 'Elasticsearch query' + ); + }); + + it('should set correct action variables', async () => { + const resp = await alertingApi.waitForDocumentInIndex({ + indexName: ALERT_ACTION_INDEX, + }); + + expect(resp.hits.hits[0]._source?.ruleId).eql(ruleId); + expect(resp.hits.hits[0]._source?.ruleName).eql('always fire'); + expect(resp.hits.hits[0]._source?.ruleParams).eql( + '{"size":100,"thresholdComparator":">","threshold":[1],"index":["kbn-data-forge-fake_hosts.fake_hosts-*"],"timeField":"@timestamp","esQuery":"{\\n \\"query\\":{\\n \\"match_all\\" : {}\\n }\\n}","timeWindowSize":1,"timeWindowUnit":"m","groupBy":"top","termField":"host.name","termSize":1,"excludeHitsFromPreviousRun":true,"aggType":"count","searchType":"esQuery"}' + ); + expect(resp.hits.hits[0]._source?.alertActionGroup).eql('query matched'); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts new file mode 100644 index 0000000000000..939ff17b775d7 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface ActionDocument { + ruleId: string; + ruleName: string; + ruleParams: string; + spaceId: string; + tags: string; + alertId: string; + alertActionGroup: string; +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts index 336fcf65c830f..8aac29fe1a485 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts @@ -8,7 +8,8 @@ import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { - describe('SLO - Burn rate rule', () => { + describe('Observability Alerting', () => { loadTestFile(require.resolve('./burn_rate_rule')); + loadTestFile(require.resolve('./es_query')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts b/x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts deleted file mode 100644 index 39edd9ba01eb9..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts +++ /dev/null @@ -1,125 +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. - */ -/* - * 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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; -import { createEsQueryRule } from '../../common/alerting/helpers/alerting_api_helper'; -import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const esClient = getService('es'); - const supertest = getService('supertest'); - const esDeleteAllIndices = getService('esDeleteAllIndices'); - const alertingApi = getService('alertingApi'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('ElasticSearch query rule', () => { - const RULE_TYPE_ID = '.es-query'; - const ALERT_ACTION_INDEX = 'alert-action-es-query'; - let actionId: string; - let ruleId: string; - - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - - after(async () => { - await supertest.delete(`/api/alerting/rule/${ruleId}`).set(internalReqHeader); - await supertest.delete(`/api/actions/connector/${actionId}`).set(internalReqHeader); - - await esClient.deleteByQuery({ - index: '.kibana-event-log-*', - query: { term: { 'rule.id': ruleId } }, - conflicts: 'proceed', - }); - await esDeleteAllIndices([ALERT_ACTION_INDEX]); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - describe('Rule creation', () => { - it('creates rule successfully', async () => { - actionId = await alertingApi.createIndexConnector({ - roleAuthc, - name: 'Index Connector: Alerting API test', - indexName: ALERT_ACTION_INDEX, - }); - - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, - roleAuthc, - internalReqHeader, - consumer: 'observability', - name: 'always fire', - ruleTypeId: RULE_TYPE_ID, - params: { - size: 100, - thresholdComparator: '>', - threshold: [-1], - index: ['alert-test-data'], - timeField: 'date', - esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, - timeWindowSize: 20, - timeWindowUnit: 's', - }, - actions: [ - { - group: 'query matched', - id: actionId, - params: { - documents: [ - { - ruleId: '{{rule.id}}', - ruleName: '{{rule.name}}', - ruleParams: '{{rule.params}}', - spaceId: '{{rule.spaceId}}', - tags: '{{rule.tags}}', - alertId: '{{alert.id}}', - alertActionGroup: '{{alert.actionGroup}}', - instanceContextValue: '{{context.instanceContextValue}}', - instanceStateValue: '{{state.instanceStateValue}}', - }, - ], - }, - frequency: { - notify_when: 'onActiveAlert', - throttle: null, - summary: false, - }, - }, - ], - }); - ruleId = createdRule.id; - expect(ruleId).not.to.be(undefined); - }); - - it('should be active', async () => { - const executionStatus = await alertingApi.waitForRuleStatus({ - roleAuthc, - ruleId, - expectedStatus: 'active', - }); - expect(executionStatus).to.be('active'); - }); - - it('should find the created rule with correct information about the consumer', async () => { - const match = await alertingApi.findRule(roleAuthc, ruleId); - expect(match).not.to.be(undefined); - expect(match.consumer).to.be('observability'); - }); - }); - }); -} From 8fcfac7102943c1ebe5206d3de99498a6fe821fa Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Mon, 16 Dec 2024 14:02:55 +0100 Subject: [PATCH 2/3] Bring back es_query_rule --- .../es_query_rule/es_query_rule.ts | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts b/x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts new file mode 100644 index 0000000000000..39edd9ba01eb9 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/observability/es_query_rule/es_query_rule.ts @@ -0,0 +1,125 @@ +/* + * 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. + */ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { createEsQueryRule } from '../../common/alerting/helpers/alerting_api_helper'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; + +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const supertest = getService('supertest'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const alertingApi = getService('alertingApi'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; + + describe('ElasticSearch query rule', () => { + const RULE_TYPE_ID = '.es-query'; + const ALERT_ACTION_INDEX = 'alert-action-es-query'; + let actionId: string; + let ruleId: string; + + before(async () => { + roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); + internalReqHeader = svlCommonApi.getInternalRequestHeader(); + }); + + after(async () => { + await supertest.delete(`/api/alerting/rule/${ruleId}`).set(internalReqHeader); + await supertest.delete(`/api/actions/connector/${actionId}`).set(internalReqHeader); + + await esClient.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'rule.id': ruleId } }, + conflicts: 'proceed', + }); + await esDeleteAllIndices([ALERT_ACTION_INDEX]); + await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); + }); + + describe('Rule creation', () => { + it('creates rule successfully', async () => { + actionId = await alertingApi.createIndexConnector({ + roleAuthc, + name: 'Index Connector: Alerting API test', + indexName: ALERT_ACTION_INDEX, + }); + + const createdRule = await createEsQueryRule({ + supertestWithoutAuth, + roleAuthc, + internalReqHeader, + consumer: 'observability', + name: 'always fire', + ruleTypeId: RULE_TYPE_ID, + params: { + size: 100, + thresholdComparator: '>', + threshold: [-1], + index: ['alert-test-data'], + timeField: 'date', + esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, + timeWindowSize: 20, + timeWindowUnit: 's', + }, + actions: [ + { + group: 'query matched', + id: actionId, + params: { + documents: [ + { + ruleId: '{{rule.id}}', + ruleName: '{{rule.name}}', + ruleParams: '{{rule.params}}', + spaceId: '{{rule.spaceId}}', + tags: '{{rule.tags}}', + alertId: '{{alert.id}}', + alertActionGroup: '{{alert.actionGroup}}', + instanceContextValue: '{{context.instanceContextValue}}', + instanceStateValue: '{{state.instanceStateValue}}', + }, + ], + }, + frequency: { + notify_when: 'onActiveAlert', + throttle: null, + summary: false, + }, + }, + ], + }); + ruleId = createdRule.id; + expect(ruleId).not.to.be(undefined); + }); + + it('should be active', async () => { + const executionStatus = await alertingApi.waitForRuleStatus({ + roleAuthc, + ruleId, + expectedStatus: 'active', + }); + expect(executionStatus).to.be('active'); + }); + + it('should find the created rule with correct information about the consumer', async () => { + const match = await alertingApi.findRule(roleAuthc, ruleId); + expect(match).not.to.be(undefined); + expect(match.consumer).to.be('observability'); + }); + }); + }); +} From 5349d72e4dd68f38d3fc5d41b6cc671393fb1f91 Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Tue, 17 Dec 2024 09:28:19 +0100 Subject: [PATCH 3/3] Remove test --- .../observability/alerting/es_query/index.ts | 14 -- .../es_query/query_dsl_with_group_by.ts | 207 ------------------ .../observability/alerting/es_query/types.ts | 16 -- .../apis/observability/alerting/index.ts | 1 - 4 files changed, 238 deletions(-) delete mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts delete mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts delete mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts deleted file mode 100644 index 680184f7c468b..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/index.ts +++ /dev/null @@ -1,14 +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 { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; - -export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { - describe('ElasticSearch query rule', () => { - loadTestFile(require.resolve('./query_dsl_with_group_by')); - }); -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts deleted file mode 100644 index db91ba5780dad..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/query_dsl_with_group_by.ts +++ /dev/null @@ -1,207 +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 expect from '@kbn/expect'; -import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services'; -import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -import { ActionDocument } from './types'; - -export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { - const esClient = getService('es'); - const logger = getService('log'); - const samlAuth = getService('samlAuth'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esDeleteAllIndices = getService('esDeleteAllIndices'); - const alertingApi = getService('alertingApi'); - const config = getService('config'); - const isServerless = config.get('serverless'); - const expectedConsumer = isServerless ? 'observability' : 'logs'; - - let adminRoleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('Query DQL with group by field', () => { - const RULE_TYPE_ID = '.es-query'; - const ALERT_ACTION_INDEX = 'alert-action-es-query'; - const RULE_ALERT_INDEX = '.alerts-stack.alerts-default'; - const INDEX = 'kbn-data-forge-fake_hosts.fake_hosts-*'; - let dataForgeConfig: PartialConfig; - let dataForgeIndices: string[]; - let actionId: string; - let ruleId: string; - - before(async () => { - adminRoleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = samlAuth.getInternalRequestHeader(); - dataForgeConfig = { - schedule: [ - { - template: 'good', - start: 'now-10m', - end: 'now+5m', - metrics: [ - { name: 'system.cpu.user.pct', method: 'linear', start: 2.5, end: 2.5 }, - { name: 'system.cpu.total.pct', method: 'linear', start: 0.5, end: 0.5 }, - { name: 'system.cpu.total.norm.pct', method: 'linear', start: 0.8, end: 0.8 }, - ], - }, - ], - indexing: { - dataset: 'fake_hosts' as Dataset, - eventsPerCycle: 1, - interval: 60000, - alignEventsToInterval: true, - }, - }; - dataForgeIndices = await generate({ client: esClient, config: dataForgeConfig, logger }); - await alertingApi.waitForDocumentInIndex({ - indexName: dataForgeIndices.join(','), - docCountTarget: 45, - }); - }); - - after(async () => { - await supertestWithoutAuth - .delete(`/api/alerting/rule/${ruleId}`) - .set(adminRoleAuthc.apiKeyHeader) - .set(internalReqHeader); - await supertestWithoutAuth - .delete(`/api/actions/connector/${actionId}`) - .set(adminRoleAuthc.apiKeyHeader) - .set(internalReqHeader); - await esClient.deleteByQuery({ - index: RULE_ALERT_INDEX, - query: { term: { 'kibana.alert.rule.uuid': ruleId } }, - conflicts: 'proceed', - }); - await esClient.deleteByQuery({ - index: '.kibana-event-log-*', - query: { term: { 'rule.id': ruleId } }, - conflicts: 'proceed', - }); - await esDeleteAllIndices([ALERT_ACTION_INDEX, ...dataForgeIndices]); - await cleanup({ client: esClient, config: dataForgeConfig, logger }); - await samlAuth.invalidateM2mApiKeyWithRoleScope(adminRoleAuthc); - }); - - describe('Rule creation', () => { - it('creates rule successfully', async () => { - actionId = await alertingApi.createIndexConnector({ - roleAuthc: adminRoleAuthc, - name: 'Index Connector: Alerting API test', - indexName: ALERT_ACTION_INDEX, - }); - - const createdRule = await alertingApi.helpers.createEsQueryRule({ - roleAuthc: adminRoleAuthc, - consumer: expectedConsumer, - name: 'always fire', - ruleTypeId: RULE_TYPE_ID, - params: { - size: 100, - thresholdComparator: '>', - threshold: [1], - index: [INDEX], - timeField: '@timestamp', - esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`, - timeWindowSize: 1, - timeWindowUnit: 'm', - groupBy: 'top', - termField: 'host.name', - termSize: 1, - }, - actions: [ - { - group: 'query matched', - id: actionId, - params: { - documents: [ - { - ruleId: '{{rule.id}}', - ruleName: '{{rule.name}}', - ruleParams: '{{rule.params}}', - spaceId: '{{rule.spaceId}}', - tags: '{{rule.tags}}', - alertId: '{{alert.id}}', - alertActionGroup: '{{alert.actionGroup}}', - instanceContextValue: '{{context.instanceContextValue}}', - instanceStateValue: '{{state.instanceStateValue}}', - }, - ], - }, - frequency: { - notify_when: 'onActiveAlert', - throttle: null, - summary: false, - }, - }, - ], - }); - ruleId = createdRule.id; - expect(ruleId).not.to.be(undefined); - }); - - it('should be active', async () => { - const executionStatus = await alertingApi.waitForRuleStatus({ - roleAuthc: adminRoleAuthc, - ruleId, - expectedStatus: 'active', - }); - expect(executionStatus).to.be('active'); - }); - - it('should find the created rule with correct information about the consumer', async () => { - const match = await alertingApi.findInRules(adminRoleAuthc, ruleId); - expect(match).not.to.be(undefined); - expect(match.consumer).to.be(expectedConsumer); - }); - - it('should set correct information in the alert document', async () => { - const resp = await alertingApi.waitForAlertInIndex({ - indexName: RULE_ALERT_INDEX, - ruleId, - }); - - expect(resp.hits.hits[0]._source).property('host.name', 'host-0'); - expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', expectedConsumer); - expect(resp.hits.hits[0]._source).property( - 'kibana.alert.reason', - 'Document count is 3 in the last 1m for host-0 in kbn-data-forge-fake_hosts.fake_hosts-* index. Alert when greater than 1.' - ); - expect(resp.hits.hits[0]._source).property( - 'kibana.alert.evaluation.conditions', - 'Number of matching documents for group "host-0" is greater than 1' - ); - expect(resp.hits.hits[0]._source).property('kibana.alert.evaluation.value', '3'); - expect(resp.hits.hits[0]._source).property('kibana.alert.evaluation.threshold', 1); - expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'always fire'); - expect(resp.hits.hits[0]._source).property('event.action', 'open'); - expect(resp.hits.hits[0]._source).property('kibana.alert.action_group', 'query matched'); - expect(resp.hits.hits[0]._source).property('kibana.alert.status', 'active'); - expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open'); - expect(resp.hits.hits[0]._source).property( - 'kibana.alert.rule.category', - 'Elasticsearch query' - ); - }); - - it('should set correct action variables', async () => { - const resp = await alertingApi.waitForDocumentInIndex({ - indexName: ALERT_ACTION_INDEX, - }); - - expect(resp.hits.hits[0]._source?.ruleId).eql(ruleId); - expect(resp.hits.hits[0]._source?.ruleName).eql('always fire'); - expect(resp.hits.hits[0]._source?.ruleParams).eql( - '{"size":100,"thresholdComparator":">","threshold":[1],"index":["kbn-data-forge-fake_hosts.fake_hosts-*"],"timeField":"@timestamp","esQuery":"{\\n \\"query\\":{\\n \\"match_all\\" : {}\\n }\\n}","timeWindowSize":1,"timeWindowUnit":"m","groupBy":"top","termField":"host.name","termSize":1,"excludeHitsFromPreviousRun":true,"aggType":"count","searchType":"esQuery"}' - ); - expect(resp.hits.hits[0]._source?.alertActionGroup).eql('query matched'); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts deleted file mode 100644 index 939ff17b775d7..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/es_query/types.ts +++ /dev/null @@ -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. - */ - -export interface ActionDocument { - ruleId: string; - ruleName: string; - ruleParams: string; - spaceId: string; - tags: string; - alertId: string; - alertActionGroup: string; -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts index 8aac29fe1a485..fd4b1962b2d84 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/index.ts @@ -10,6 +10,5 @@ import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_cont export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { describe('Observability Alerting', () => { loadTestFile(require.resolve('./burn_rate_rule')); - loadTestFile(require.resolve('./es_query')); }); }