From 4dcabbc7a34d52ceafcae1c793431427ebc3ad72 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Thu, 5 Sep 2024 15:54:07 +0100 Subject: [PATCH 01/14] [FTR] Collapse Alerting API Helpers Resolves: https://github.com/elastic/kibana/issues/192201 --- .../api_integration/services/alerting_api.ts | 827 +++++++++++++++++- .../common/alerting/alert_documents.ts | 35 +- .../alerting/helpers/alerting_api_helper.ts | 478 ---------- .../helpers/alerting_wait_for_helpers.ts | 402 --------- .../test_suites/common/alerting/rules.ts | 225 ++--- .../common/alerting/summary_actions.ts | 63 +- .../es_query_rule/es_query_rule.ts | 6 +- .../functional/services/index.ts | 3 +- .../observability/rules/rules_list.ts | 221 +++-- .../test_suites/search/rules/rule_details.ts | 56 +- 10 files changed, 1054 insertions(+), 1262 deletions(-) delete mode 100644 x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts delete mode 100644 x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts diff --git a/x-pack/test_serverless/api_integration/services/alerting_api.ts b/x-pack/test_serverless/api_integration/services/alerting_api.ts index 6000e9d8bdc88..4e97ac2afb74b 100644 --- a/x-pack/test_serverless/api_integration/services/alerting_api.ts +++ b/x-pack/test_serverless/api_integration/services/alerting_api.ts @@ -5,17 +5,38 @@ * 2.0. */ +import moment from 'moment'; +import pRetry from 'p-retry'; import type { AggregationsAggregate, SearchResponse, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - +import type { Client } from '@elastic/elasticsearch'; import { MetricThresholdParams } from '@kbn/infra-plugin/common/alerting/metrics'; import { ThresholdParams } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { v4 as uuidv4 } from 'uuid'; import { RoleCredentials } from '../../shared/services'; import { SloBurnRateRuleParams } from './slo_api'; import { FtrProviderContext } from '../ftr_provider_context'; +interface CreateEsQueryRuleParams { + size: number; + thresholdComparator: string; + threshold: number[]; + timeWindowSize?: number; + timeWindowUnit?: string; + esQuery?: string; + timeField?: string; + searchConfiguration?: unknown; + indexName?: string; + excludeHitsFromPreviousRun?: boolean; + aggType?: string; + aggField?: string; + groupBy?: string; + termField?: string; + termSize?: number; + index?: string[]; +} export function AlertingApiProvider({ getService }: FtrProviderContext) { const retry = getService('retry'); const es = getService('es'); @@ -25,7 +46,809 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { const svlCommonApi = getService('svlCommonApi'); const supertestWithoutAuth = getService('supertestWithoutAuth'); + const generateUniqueKey = () => uuidv4().replace(/-/g, ''); + + const helpers = { + async waitForAlertInIndex({ + esClient, + filter, + indexName, + ruleId, + num = 1, + }: { + esClient: Client; + filter: Date; + indexName: string; + ruleId: string; + num: number; + }): Promise>> { + return await pRetry( + async () => { + const response = await esClient.search({ + index: indexName, + body: { + query: { + bool: { + must: [ + { + term: { + 'kibana.alert.rule.uuid': ruleId, + }, + }, + { + range: { + '@timestamp': { + gte: filter.getTime().toString(), + }, + }, + }, + ], + }, + }, + }, + }); + if (response.hits.hits.length < num) { + throw new Error(`Only found ${response.hits.hits.length} / ${num} documents`); + } + return response; + }, + { retries: 10 } + ); + }, + + async waitForDocumentInIndex({ + esClient, + indexName, + ruleId, + num = 1, + sort = 'desc', + }: { + esClient: Client; + indexName: string; + ruleId: string; + num?: number; + sort?: 'asc' | 'desc'; + }): Promise { + return await pRetry( + async () => { + const response = await esClient.search({ + index: indexName, + sort: `date:${sort}`, + body: { + query: { + bool: { + must: [ + { + term: { + 'ruleId.keyword': ruleId, + }, + }, + ], + }, + }, + }, + }); + if (response.hits.hits.length < num) { + throw new Error(`Only found ${response.hits.hits.length} / ${num} documents`); + } + return response; + }, + { retries: 10 } + ); + }, + + async createIndexConnector({ + roleAuthc, + name, + indexName, + }: { + roleAuthc: RoleCredentials; + name: string; + indexName: string; + }) { + const { body } = await supertestWithoutAuth + .post(`/api/actions/connector`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + name, + config: { + index: indexName, + refresh: true, + }, + connector_type_id: '.index', + }) + .expect(200); + return body; + }, + + async createSlackConnector({ roleAuthc, name }: { roleAuthc: RoleCredentials; name: string }) { + const { body } = await supertestWithoutAuth + .post(`/api/actions/connector`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + name, + config: {}, + secrets: { + webhookUrl: 'http://test', + }, + connector_type_id: '.slack', + }) + .expect(200); + return body; + }, + + async createEsQueryRule({ + roleAuthc, + name, + ruleTypeId, + params, + actions = [], + tags = [], + schedule, + consumer, + notifyWhen, + enabled = true, + }: { + roleAuthc: RoleCredentials; + ruleTypeId: string; + name: string; + params: CreateEsQueryRuleParams; + consumer: string; + actions?: any[]; + tags?: any[]; + schedule?: { interval: string }; + notifyWhen?: string; + enabled?: boolean; + }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + enabled, + params, + consumer, + schedule: schedule || { + interval: '1h', + }, + tags, + name, + rule_type_id: ruleTypeId, + actions, + ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), + }) + .expect(200); + return body; + }, + + async createAnomalyRule({ + roleAuthc, + name = generateUniqueKey(), + actions = [], + tags = ['foo', 'bar'], + schedule, + consumer = 'alerts', + notifyWhen, + enabled = true, + ruleTypeId = 'apm.anomaly', + params, + }: { + roleAuthc: RoleCredentials; + name?: string; + consumer?: string; + actions?: any[]; + tags?: any[]; + schedule?: { interval: string }; + notifyWhen?: string; + enabled?: boolean; + ruleTypeId?: string; + params?: any; + }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + enabled, + params: params || { + anomalySeverityType: 'critical', + anomalyDetectorTypes: ['txLatency'], + environment: 'ENVIRONMENT_ALL', + windowSize: 30, + windowUnit: 'm', + }, + consumer, + schedule: schedule || { + interval: '1m', + }, + tags, + name, + rule_type_id: ruleTypeId, + actions, + ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), + }) + .expect(200); + return body; + }, + + async createLatencyThresholdRule({ + roleAuthc, + name = generateUniqueKey(), + actions = [], + tags = ['foo', 'bar'], + schedule, + consumer = 'apm', + notifyWhen, + enabled = true, + ruleTypeId = 'apm.transaction_duration', + params, + }: { + roleAuthc: RoleCredentials; + name?: string; + consumer?: string; + actions?: any[]; + tags?: any[]; + schedule?: { interval: string }; + notifyWhen?: string; + enabled?: boolean; + ruleTypeId?: string; + params?: any; + }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + enabled, + params: params || { + aggregationType: 'avg', + environment: 'ENVIRONMENT_ALL', + threshold: 1500, + windowSize: 5, + windowUnit: 'm', + }, + consumer, + schedule: schedule || { + interval: '1m', + }, + tags, + name, + rule_type_id: ruleTypeId, + actions, + ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), + }); + return body; + }, + + async createInventoryRule({ + roleAuthc, + name = generateUniqueKey(), + actions = [], + tags = ['foo', 'bar'], + schedule, + consumer = 'alerts', + notifyWhen, + enabled = true, + ruleTypeId = 'metrics.alert.inventory.threshold', + params, + }: { + roleAuthc: RoleCredentials; + name?: string; + consumer?: string; + actions?: any[]; + tags?: any[]; + schedule?: { interval: string }; + notifyWhen?: string; + enabled?: boolean; + ruleTypeId?: string; + params?: any; + }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + enabled, + params: params || { + nodeType: 'host', + criteria: [ + { + metric: 'cpu', + comparator: '>', + threshold: [5], + timeSize: 1, + timeUnit: 'm', + customMetric: { + type: 'custom', + id: 'alert-custom-metric', + field: '', + aggregation: 'avg', + }, + }, + ], + sourceId: 'default', + }, + consumer, + schedule: schedule || { + interval: '1m', + }, + tags, + name, + rule_type_id: ruleTypeId, + actions, + ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), + }) + .expect(200); + return body; + }, + + async disableRule({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule/${ruleId}/_disable`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(204); + return body; + }, + + async updateEsQueryRule({ + roleAuthc, + ruleId, + updates, + }: { + roleAuthc: RoleCredentials; + ruleId: string; + updates: any; + }) { + const { body: r } = await supertestWithoutAuth + .get(`/api/alerting/rule/${ruleId}`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + const body = await supertestWithoutAuth + .put(`/api/alerting/rule/${ruleId}`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + ...{ + name: r.name, + schedule: r.schedule, + throttle: r.throttle, + tags: r.tags, + params: r.params, + notify_when: r.notifyWhen, + actions: r.actions.map((action: any) => ({ + group: action.group, + params: action.params, + id: action.id, + frequency: action.frequency, + })), + }, + ...updates, + }) + .expect(200); + return body; + }, + + async runRule({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + const response = await supertestWithoutAuth + .post(`/internal/alerting/rule/${ruleId}/_run_soon`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(204); + return response; + }, + + async waitForNumRuleRuns({ + roleAuthc, + numOfRuns, + ruleId, + esClient, + testStart, + }: { + roleAuthc: RoleCredentials; + numOfRuns: number; + ruleId: string; + esClient: Client; + testStart: Date; + }) { + for (let i = 0; i < numOfRuns; i++) { + await pRetry( + async () => { + await this.runRule({ roleAuthc, ruleId }); + await this.waiting.waitForExecutionEventLog({ + esClient, + filter: testStart, + ruleId, + num: i + 1, + }); + await this.waiting.waitForAllTasksIdle({ esClient, filter: testStart }); + }, + { retries: 10 } + ); + } + }, + + async muteRule({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule/${ruleId}/_mute_all`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(204); + return body; + }, + + async enableRule({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule/${ruleId}/_enable`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(204); + return body; + }, + + async muteAlert({ + roleAuthc, + ruleId, + alertId, + }: { + roleAuthc: RoleCredentials; + ruleId: string; + alertId: string; + }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule/${ruleId}/alert/${alertId}/_mute`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(204); + return body; + }, + + async unmuteRule({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + const { body } = await supertestWithoutAuth + .post(`/api/alerting/rule/${ruleId}/_unmute_all`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(204); + return body; + }, + + async snoozeRule({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + const { body } = await supertestWithoutAuth + .post(`/internal/alerting/rule/${ruleId}/_snooze`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .send({ + snooze_schedule: { + duration: 100000000, + rRule: { + count: 1, + dtstart: moment().format(), + tzid: 'UTC', + }, + }, + }) + .expect(204); + return body; + }, + + async findRule({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + if (!ruleId) { + throw new Error(`'ruleId' is undefined`); + } + const response = await supertestWithoutAuth + .get(`/api/alerting/rule/${ruleId}`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader); + return response.body || {}; + }, + + waiting: { + async waitForDocumentInIndex({ + esClient, + indexName, + ruleId, + num = 1, + sort = 'desc', + }: { + esClient: Client; + indexName: string; + ruleId: string; + num?: number; + sort?: 'asc' | 'desc'; + }): Promise { + return await pRetry( + async () => { + const response = await esClient.search({ + index: indexName, + sort: `date:${sort}`, + body: { + query: { + bool: { + must: [ + { + term: { + 'ruleId.keyword': ruleId, + }, + }, + ], + }, + }, + }, + }); + if (response.hits.hits.length < num) { + throw new Error(`Only found ${response.hits.hits.length} / ${num} documents`); + } + return response; + }, + { retries: 10 } + ); + }, + + async getDocumentsInIndex({ + esClient, + indexName, + ruleId, + }: { + esClient: Client; + indexName: string; + ruleId: string; + }): Promise { + return await esClient.search({ + index: indexName, + body: { + query: { + bool: { + must: [ + { + term: { + 'ruleId.keyword': ruleId, + }, + }, + ], + }, + }, + }, + }); + }, + + async waitForAllTasksIdle({ + esClient, + filter, + }: { + esClient: Client; + filter: Date; + }): Promise { + return await pRetry( + async () => { + const response = await esClient.search({ + index: '.kibana_task_manager', + body: { + query: { + bool: { + must: [ + { + terms: { + 'task.scope': ['actions', 'alerting'], + }, + }, + { + range: { + 'task.scheduledAt': { + gte: filter.getTime().toString(), + }, + }, + }, + ], + must_not: [ + { + term: { + 'task.status': 'idle', + }, + }, + ], + }, + }, + }, + }); + if (response.hits.hits.length !== 0) { + throw new Error(`Expected 0 hits but received ${response.hits.hits.length}`); + } + return response; + }, + { retries: 10 } + ); + }, + + async waitForExecutionEventLog({ + esClient, + filter, + ruleId, + num = 1, + }: { + esClient: Client; + filter: Date; + ruleId: string; + num?: number; + }): Promise { + return await pRetry( + async () => { + const response = await esClient.search({ + index: '.kibana-event-log*', + body: { + query: { + bool: { + filter: [ + { + term: { + 'rule.id': { + value: ruleId, + }, + }, + }, + { + term: { + 'event.provider': { + value: 'alerting', + }, + }, + }, + { + term: { + 'event.action': 'execute', + }, + }, + { + range: { + '@timestamp': { + gte: filter.getTime().toString(), + }, + }, + }, + ], + }, + }, + }, + }); + if (response.hits.hits.length < num) { + throw new Error('No hits found'); + } + return response; + }, + { retries: 10 } + ); + }, + + async createIndex({ esClient, indexName }: { esClient: Client; indexName: string }) { + return await esClient.indices.create( + { + index: indexName, + body: {}, + }, + { meta: true } + ); + }, + + async waitForAllTasks({ + esClient, + filter, + taskType, + attempts, + }: { + esClient: Client; + filter: Date; + taskType: string; + attempts: number; + }): Promise { + return await pRetry( + async () => { + const response = await esClient.search({ + index: '.kibana_task_manager', + body: { + query: { + bool: { + must: [ + { + term: { + 'task.status': 'idle', + }, + }, + { + term: { + 'task.attempts': attempts, + }, + }, + { + terms: { + 'task.scope': ['actions', 'alerting'], + }, + }, + { + term: { + 'task.taskType': taskType, + }, + }, + { + range: { + 'task.scheduledAt': { + gte: filter.getTime().toString(), + }, + }, + }, + ], + }, + }, + }, + }); + if (response.hits.hits.length === 0) { + throw new Error('No hits found'); + } + return response; + }, + { retries: 10 } + ); + }, + + async waitForDisabled({ + esClient, + ruleId, + filter, + }: { + esClient: Client; + ruleId: string; + filter: Date; + }): Promise { + return await pRetry( + async () => { + const response = await esClient.search({ + index: '.kibana_task_manager', + body: { + query: { + bool: { + must: [ + { + term: { + 'task.id': `task:${ruleId}`, + }, + }, + { + terms: { + 'task.scope': ['actions', 'alerting'], + }, + }, + { + range: { + 'task.scheduledAt': { + gte: filter.getTime().toString(), + }, + }, + }, + { + term: { + 'task.enabled': true, + }, + }, + ], + }, + }, + }, + }); + if (response.hits.hits.length !== 0) { + throw new Error(`Expected 0 hits but received ${response.hits.hits.length}`); + } + return response; + }, + { retries: 10 } + ); + }, + }, + }; + return { + helpers, + async waitForRuleStatus({ roleAuthc, ruleId, @@ -65,7 +888,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { index: indexName, rest_total_hits_as_int: true, }); - logger.debug(`Found ${response.hits.total} docs, looking for atleast ${docCountTarget}.`); + logger.debug(`Found ${response.hits.total} docs, looking for at least ${docCountTarget}.`); if (!response.hits.total || (response.hits.total as number) < docCountTarget) { throw new Error('No hits found'); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts index 93dd4e5565db5..cf727fd9fd1bc 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/alert_documents.ts @@ -42,22 +42,18 @@ import { ALERT_PREVIOUS_ACTION_GROUP, } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { createEsQueryRule } from './helpers/alerting_api_helper'; -import { waitForAlertInIndex, waitForNumRuleRuns } from './helpers/alerting_wait_for_helpers'; import { ObjectRemover } from '../../../../shared/lib'; -import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; +import { RoleCredentials } from '../../../../shared/services'; const OPEN_OR_ACTIVE = new Set(['open', 'active']); export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); let roleAdmin: RoleCredentials; - let internalReqHeader: InternalRequestHeader; const supertest = getService('supertest'); const esClient = getService('es'); const objectRemover = new ObjectRemover(supertest); + const alertingApi = getService('alertingApi'); describe('Alert documents', function () { // Timeout of 360000ms exceeded @@ -68,7 +64,6 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { roleAdmin = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); }); afterEach(async () => { @@ -80,10 +75,8 @@ export default function ({ getService }: FtrProviderContext) { }); it('should generate an alert document for an active alert', async () => { - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -103,17 +96,15 @@ export default function ({ getService }: FtrProviderContext) { // get the first alert document written const testStart1 = new Date(); - await waitForNumRuleRuns({ - supertestWithoutAuth, + await alertingApi.helpers.waitForNumRuleRuns({ roleAuthc: roleAdmin, - internalReqHeader, numOfRuns: 1, ruleId, esClient, testStart: testStart1, }); - const alResp1 = await waitForAlertInIndex({ + const alResp1 = await alertingApi.helpers.waitForAlertInIndex({ esClient, filter: testStart1, indexName: ALERT_INDEX, @@ -206,10 +197,8 @@ export default function ({ getService }: FtrProviderContext) { }); it('should update an alert document for an ongoing alert', async () => { - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -229,17 +218,15 @@ export default function ({ getService }: FtrProviderContext) { // get the first alert document written const testStart1 = new Date(); - await waitForNumRuleRuns({ - supertestWithoutAuth, + await alertingApi.helpers.waitForNumRuleRuns({ roleAuthc: roleAdmin, - internalReqHeader, numOfRuns: 1, ruleId, esClient, testStart: testStart1, }); - const alResp1 = await waitForAlertInIndex({ + const alResp1 = await alertingApi.helpers.waitForAlertInIndex({ esClient, filter: testStart1, indexName: ALERT_INDEX, @@ -249,17 +236,15 @@ export default function ({ getService }: FtrProviderContext) { // wait for another run, get the updated alert document const testStart2 = new Date(); - await waitForNumRuleRuns({ - supertestWithoutAuth, + await alertingApi.helpers.waitForNumRuleRuns({ roleAuthc: roleAdmin, - internalReqHeader, numOfRuns: 1, ruleId, esClient, testStart: testStart2, }); - const alResp2 = await waitForAlertInIndex({ + const alResp2 = await alertingApi.helpers.waitForAlertInIndex({ esClient, filter: testStart2, indexName: ALERT_INDEX, diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts deleted file mode 100644 index f7a909c688d0e..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts +++ /dev/null @@ -1,478 +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 moment from 'moment'; -import { v4 as uuidv4 } from 'uuid'; -import type { Agent as SuperTestAgent } from 'supertest'; -import { - InternalRequestHeader, - RoleCredentials, - SupertestWithoutAuthProviderType, -} from '../../../../../shared/services'; - -interface CreateEsQueryRuleParams { - size: number; - thresholdComparator: string; - threshold: number[]; - timeWindowSize?: number; - timeWindowUnit?: string; - esQuery?: string; - timeField?: string; - searchConfiguration?: unknown; - indexName?: string; - excludeHitsFromPreviousRun?: boolean; - aggType?: string; - aggField?: string; - groupBy?: string; - termField?: string; - termSize?: number; - index?: string[]; -} - -export async function createIndexConnector({ - supertestWithoutAuth, - roleAuthc, - internalReqHeader, - name, - indexName, -}: { - supertestWithoutAuth: SupertestWithoutAuthProviderType; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; - name: string; - indexName: string; -}) { - const { body } = await supertestWithoutAuth - .post(`/api/actions/connector`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name, - config: { - index: indexName, - refresh: true, - }, - connector_type_id: '.index', - }) - .expect(200); - return body; -} - -export async function createSlackConnector({ - supertestWithoutAuth, - roleAuthc, - internalReqHeader, - name, -}: { - supertestWithoutAuth: SupertestWithoutAuthProviderType; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; - name: string; -}) { - const { body } = await supertestWithoutAuth - .post(`/api/actions/connector`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name, - config: {}, - secrets: { - webhookUrl: 'http://test', - }, - connector_type_id: '.slack', - }) - .expect(200); - return body; -} - -export async function createEsQueryRule({ - supertestWithoutAuth, - roleAuthc, - internalReqHeader, - name, - ruleTypeId, - params, - actions = [], - tags = [], - schedule, - consumer, - notifyWhen, - enabled = true, -}: { - supertestWithoutAuth: SupertestWithoutAuthProviderType; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; - ruleTypeId: string; - name: string; - params: CreateEsQueryRuleParams; - consumer: string; - actions?: any[]; - tags?: any[]; - schedule?: { interval: string }; - notifyWhen?: string; - enabled?: boolean; -}) { - const { body } = await supertestWithoutAuth - .post(`/api/alerting/rule`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - enabled, - params, - consumer, - schedule: schedule || { - interval: '1h', - }, - tags, - name, - rule_type_id: ruleTypeId, - actions, - ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), - }) - .expect(200); - return body; -} - -export const generateUniqueKey = () => uuidv4().replace(/-/g, ''); - -export async function createAnomalyRule({ - supertest, - name = generateUniqueKey(), - actions = [], - tags = ['foo', 'bar'], - schedule, - consumer = 'alerts', - notifyWhen, - enabled = true, - ruleTypeId = 'apm.anomaly', - params, -}: { - supertest: SuperTestAgent; - name?: string; - consumer?: string; - actions?: any[]; - tags?: any[]; - schedule?: { interval: string }; - notifyWhen?: string; - enabled?: boolean; - ruleTypeId?: string; - params?: any; -}) { - const { body } = await supertest - .post(`/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .send({ - enabled, - params: params || { - anomalySeverityType: 'critical', - anomalyDetectorTypes: ['txLatency'], - environment: 'ENVIRONMENT_ALL', - windowSize: 30, - windowUnit: 'm', - }, - consumer, - schedule: schedule || { - interval: '1m', - }, - tags, - name, - rule_type_id: ruleTypeId, - actions, - ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), - }) - .expect(200); - return body; -} - -export async function createLatencyThresholdRule({ - supertest, - name = generateUniqueKey(), - actions = [], - tags = ['foo', 'bar'], - schedule, - consumer = 'apm', - notifyWhen, - enabled = true, - ruleTypeId = 'apm.transaction_duration', - params, -}: { - supertest: SuperTestAgent; - name?: string; - consumer?: string; - actions?: any[]; - tags?: any[]; - schedule?: { interval: string }; - notifyWhen?: string; - enabled?: boolean; - ruleTypeId?: string; - params?: any; -}) { - const { body } = await supertest - .post(`/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .send({ - enabled, - params: params || { - aggregationType: 'avg', - environment: 'ENVIRONMENT_ALL', - threshold: 1500, - windowSize: 5, - windowUnit: 'm', - }, - consumer, - schedule: schedule || { - interval: '1m', - }, - tags, - name, - rule_type_id: ruleTypeId, - actions, - ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), - }); - return body; -} - -export async function createInventoryRule({ - supertest, - name = generateUniqueKey(), - actions = [], - tags = ['foo', 'bar'], - schedule, - consumer = 'alerts', - notifyWhen, - enabled = true, - ruleTypeId = 'metrics.alert.inventory.threshold', - params, -}: { - supertest: SuperTestAgent; - name?: string; - consumer?: string; - actions?: any[]; - tags?: any[]; - schedule?: { interval: string }; - notifyWhen?: string; - enabled?: boolean; - ruleTypeId?: string; - params?: any; -}) { - const { body } = await supertest - .post(`/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .send({ - enabled, - params: params || { - nodeType: 'host', - criteria: [ - { - metric: 'cpu', - comparator: '>', - threshold: [5], - timeSize: 1, - timeUnit: 'm', - customMetric: { - type: 'custom', - id: 'alert-custom-metric', - field: '', - aggregation: 'avg', - }, - }, - ], - sourceId: 'default', - }, - consumer, - schedule: schedule || { - interval: '1m', - }, - tags, - name, - rule_type_id: ruleTypeId, - actions, - ...(notifyWhen ? { notify_when: notifyWhen, throttle: '5m' } : {}), - }) - .expect(200); - return body; -} - -export async function disableRule({ - supertest, - ruleId, -}: { - supertest: SuperTestAgent; - ruleId: string; -}) { - const { body } = await supertest - .post(`/api/alerting/rule/${ruleId}/_disable`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .expect(204); - return body; -} - -export async function updateEsQueryRule({ - supertest, - ruleId, - updates, -}: { - supertest: SuperTestAgent; - ruleId: string; - updates: any; -}) { - const { body: r } = await supertest - .get(`/api/alerting/rule/${ruleId}`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .expect(200); - const body = await supertest - .put(`/api/alerting/rule/${ruleId}`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .send({ - ...{ - name: r.name, - schedule: r.schedule, - throttle: r.throttle, - tags: r.tags, - params: r.params, - notify_when: r.notifyWhen, - actions: r.actions.map((action: any) => ({ - group: action.group, - params: action.params, - id: action.id, - frequency: action.frequency, - })), - }, - ...updates, - }) - .expect(200); - return body; -} - -export async function runRule({ - supertestWithoutAuth, - roleAuthc, - internalReqHeader, - ruleId, -}: { - supertestWithoutAuth: SupertestWithoutAuthProviderType; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; - ruleId: string; -}) { - const response = await supertestWithoutAuth - .post(`/internal/alerting/rule/${ruleId}/_run_soon`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .expect(204); - return response; -} - -export async function muteRule({ - supertest, - ruleId, -}: { - supertest: SuperTestAgent; - ruleId: string; -}) { - const { body } = await supertest - .post(`/api/alerting/rule/${ruleId}/_mute_all`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .expect(204); - return body; -} - -export async function enableRule({ - supertest, - ruleId, -}: { - supertest: SuperTestAgent; - ruleId: string; -}) { - const { body } = await supertest - .post(`/api/alerting/rule/${ruleId}/_enable`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .expect(204); - return body; -} - -export async function muteAlert({ - supertest, - ruleId, - alertId, -}: { - supertest: SuperTestAgent; - ruleId: string; - alertId: string; -}) { - const { body } = await supertest - .post(`/api/alerting/rule/${ruleId}/alert/${alertId}/_mute`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .expect(204); - return body; -} - -export async function unmuteRule({ - supertest, - ruleId, -}: { - supertest: SuperTestAgent; - ruleId: string; -}) { - const { body } = await supertest - .post(`/api/alerting/rule/${ruleId}/_unmute_all`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .expect(204); - return body; -} - -export async function snoozeRule({ - supertest, - ruleId, -}: { - supertest: SuperTestAgent; - ruleId: string; -}) { - const { body } = await supertest - .post(`/internal/alerting/rule/${ruleId}/_snooze`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo') - .send({ - snooze_schedule: { - duration: 100000000, - rRule: { - count: 1, - dtstart: moment().format(), - tzid: 'UTC', - }, - }, - }) - .expect(204); - return body; -} - -export async function findRule({ - supertest, - ruleId, -}: { - supertest: SuperTestAgent; - ruleId: string; -}) { - if (!ruleId) { - throw new Error(`'ruleId' is undefined`); - } - const response = await supertest - .get(`/api/alerting/rule/${ruleId}`) - .set('kbn-xsrf', 'foo') - .set('x-elastic-internal-origin', 'foo'); - return response.body || {}; -} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts deleted file mode 100644 index c7f2ac357e4a2..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts +++ /dev/null @@ -1,402 +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 pRetry from 'p-retry'; -import type { Client } from '@elastic/elasticsearch'; -import type { - AggregationsAggregate, - SearchResponse, -} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { runRule } from './alerting_api_helper'; -import type { SupertestWithoutAuthProviderType } from '../../../../../shared/services'; -import { RoleCredentials } from '../../../../../shared/services'; -import { InternalRequestHeader } from '../../../../../shared/services'; - -export async function waitForDocumentInIndex({ - esClient, - indexName, - ruleId, - num = 1, - sort = 'desc', -}: { - esClient: Client; - indexName: string; - ruleId: string; - num?: number; - sort?: 'asc' | 'desc'; -}): Promise { - return await pRetry( - async () => { - const response = await esClient.search({ - index: indexName, - sort: `date:${sort}`, - body: { - query: { - bool: { - must: [ - { - term: { - 'ruleId.keyword': ruleId, - }, - }, - ], - }, - }, - }, - }); - if (response.hits.hits.length < num) { - throw new Error(`Only found ${response.hits.hits.length} / ${num} documents`); - } - return response; - }, - { retries: 10 } - ); -} - -export async function getDocumentsInIndex({ - esClient, - indexName, - ruleId, -}: { - esClient: Client; - indexName: string; - ruleId: string; -}): Promise { - return await esClient.search({ - index: indexName, - body: { - query: { - bool: { - must: [ - { - term: { - 'ruleId.keyword': ruleId, - }, - }, - ], - }, - }, - }, - }); -} - -export async function createIndex({ - esClient, - indexName, -}: { - esClient: Client; - indexName: string; -}) { - return await esClient.indices.create( - { - index: indexName, - body: {}, - }, - { meta: true } - ); -} - -export async function waitForAlertInIndex({ - esClient, - filter, - indexName, - ruleId, - num = 1, -}: { - esClient: Client; - filter: Date; - indexName: string; - ruleId: string; - num: number; -}): Promise>> { - return await pRetry( - async () => { - const response = await esClient.search({ - index: indexName, - body: { - query: { - bool: { - must: [ - { - term: { - 'kibana.alert.rule.uuid': ruleId, - }, - }, - { - range: { - '@timestamp': { - gte: filter.getTime().toString(), - }, - }, - }, - ], - }, - }, - }, - }); - if (response.hits.hits.length < num) { - throw new Error(`Only found ${response.hits.hits.length} / ${num} documents`); - } - return response; - }, - { retries: 10 } - ); -} - -export async function waitForAllTasksIdle({ - esClient, - filter, -}: { - esClient: Client; - filter: Date; -}): Promise { - return await pRetry( - async () => { - const response = await esClient.search({ - index: '.kibana_task_manager', - body: { - query: { - bool: { - must: [ - { - terms: { - 'task.scope': ['actions', 'alerting'], - }, - }, - { - range: { - 'task.scheduledAt': { - gte: filter.getTime().toString(), - }, - }, - }, - ], - must_not: [ - { - term: { - 'task.status': 'idle', - }, - }, - ], - }, - }, - }, - }); - if (response.hits.hits.length !== 0) { - throw new Error(`Expected 0 hits but received ${response.hits.hits.length}`); - } - return response; - }, - { retries: 10 } - ); -} - -export async function waitForAllTasks({ - esClient, - filter, - taskType, - attempts, -}: { - esClient: Client; - filter: Date; - taskType: string; - attempts: number; -}): Promise { - return await pRetry( - async () => { - const response = await esClient.search({ - index: '.kibana_task_manager', - body: { - query: { - bool: { - must: [ - { - term: { - 'task.status': 'idle', - }, - }, - { - term: { - 'task.attempts': attempts, - }, - }, - { - terms: { - 'task.scope': ['actions', 'alerting'], - }, - }, - { - term: { - 'task.taskType': taskType, - }, - }, - { - range: { - 'task.scheduledAt': { - gte: filter.getTime().toString(), - }, - }, - }, - ], - }, - }, - }, - }); - if (response.hits.hits.length === 0) { - throw new Error('No hits found'); - } - return response; - }, - { retries: 10 } - ); -} - -export async function waitForDisabled({ - esClient, - ruleId, - filter, -}: { - esClient: Client; - ruleId: string; - filter: Date; -}): Promise { - return await pRetry( - async () => { - const response = await esClient.search({ - index: '.kibana_task_manager', - body: { - query: { - bool: { - must: [ - { - term: { - 'task.id': `task:${ruleId}`, - }, - }, - { - terms: { - 'task.scope': ['actions', 'alerting'], - }, - }, - { - range: { - 'task.scheduledAt': { - gte: filter.getTime().toString(), - }, - }, - }, - { - term: { - 'task.enabled': true, - }, - }, - ], - }, - }, - }, - }); - if (response.hits.hits.length !== 0) { - throw new Error(`Expected 0 hits but received ${response.hits.hits.length}`); - } - return response; - }, - { retries: 10 } - ); -} - -export async function waitForExecutionEventLog({ - esClient, - filter, - ruleId, - num = 1, -}: { - esClient: Client; - filter: Date; - ruleId: string; - num?: number; -}): Promise { - return await pRetry( - async () => { - const response = await esClient.search({ - index: '.kibana-event-log*', - body: { - query: { - bool: { - filter: [ - { - term: { - 'rule.id': { - value: ruleId, - }, - }, - }, - { - term: { - 'event.provider': { - value: 'alerting', - }, - }, - }, - { - term: { - 'event.action': 'execute', - }, - }, - { - range: { - '@timestamp': { - gte: filter.getTime().toString(), - }, - }, - }, - ], - }, - }, - }, - }); - if (response.hits.hits.length < num) { - throw new Error('No hits found'); - } - return response; - }, - { retries: 10 } - ); -} - -export async function waitForNumRuleRuns({ - supertestWithoutAuth, - roleAuthc, - internalReqHeader, - numOfRuns, - ruleId, - esClient, - testStart, -}: { - supertestWithoutAuth: SupertestWithoutAuthProviderType; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; - numOfRuns: number; - ruleId: string; - esClient: Client; - testStart: Date; -}) { - for (let i = 0; i < numOfRuns; i++) { - await pRetry( - async () => { - await runRule({ supertestWithoutAuth, roleAuthc, internalReqHeader, ruleId }); - await waitForExecutionEventLog({ - esClient, - filter: testStart, - ruleId, - num: i + 1, - }); - await waitForAllTasksIdle({ esClient, filter: testStart }); - }, - { retries: 10 } - ); - } -} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts index 37b78a5e1b36f..d8304ad3e5b5d 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts @@ -9,28 +9,6 @@ import { RULE_SAVED_OBJECT_TYPE } from '@kbn/alerting-plugin/server'; import expect from '@kbn/expect'; import { omit } from 'lodash'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { - createIndexConnector, - createEsQueryRule, - disableRule, - updateEsQueryRule, - runRule, - muteRule, - enableRule, - muteAlert, - unmuteRule, - createSlackConnector, -} from './helpers/alerting_api_helper'; -import { - createIndex, - getDocumentsInIndex, - waitForAllTasks, - waitForAllTasksIdle, - waitForDisabled, - waitForDocumentInIndex, - waitForExecutionEventLog, - waitForNumRuleRuns, -} from './helpers/alerting_wait_for_helpers'; import type { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { @@ -39,7 +17,8 @@ export default function ({ getService }: FtrProviderContext) { const esDeleteAllIndices = getService('esDeleteAllIndices'); const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); + const alertingApi = getService('alertingApi'); + let roleAdmin: RoleCredentials; let internalReqHeader: InternalRequestHeader; @@ -73,19 +52,15 @@ export default function ({ getService }: FtrProviderContext) { it('should schedule task, run rule and schedule actions when appropriate', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -130,7 +105,7 @@ export default function ({ getService }: FtrProviderContext) { ruleId = createdRule.id; // Wait for the action to index a document before disabling the alert and waiting for tasks to finish - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -151,7 +126,7 @@ export default function ({ getService }: FtrProviderContext) { tags: '', }); - const eventLogResp = await waitForExecutionEventLog({ + const eventLogResp = await alertingApi.helpers.waiting.waitForExecutionEventLog({ esClient, filter: testStart, ruleId, @@ -171,19 +146,15 @@ export default function ({ getService }: FtrProviderContext) { it('should pass updated rule params to executor', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -228,7 +199,7 @@ export default function ({ getService }: FtrProviderContext) { ruleId = createdRule.id; // Wait for the action to index a document before disabling the alert and waiting for tasks to finish - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -249,13 +220,13 @@ export default function ({ getService }: FtrProviderContext) { tags: '', }); - await waitForAllTasksIdle({ + await alertingApi.helpers.waiting.waitForAllTasksIdle({ esClient, filter: testStart, }); - await updateEsQueryRule({ - supertest, + await alertingApi.helpers.updateEsQueryRule({ + roleAuthc: roleAdmin, ruleId, updates: { name: 'def', @@ -263,15 +234,13 @@ export default function ({ getService }: FtrProviderContext) { }, }); - await runRule({ - supertestWithoutAuth, + await alertingApi.helpers.runRule({ roleAuthc: roleAdmin, - internalReqHeader, ruleId, }); // make sure alert info passed to executor is correct - const resp2 = await waitForDocumentInIndex({ + const resp2 = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -298,18 +267,14 @@ export default function ({ getService }: FtrProviderContext) { const testStart = new Date(); // Should fail - const createdConnector = await createSlackConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createSlackConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Slack Connector: Alerting API test', }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -341,7 +306,7 @@ export default function ({ getService }: FtrProviderContext) { ruleId = createdRule.id; // Should retry when the the action fails - const resp = await waitForAllTasks({ + const resp = await alertingApi.helpers.waiting.waitForAllTasks({ esClient, filter: testStart, taskType: 'actions:.slack', @@ -353,19 +318,15 @@ export default function ({ getService }: FtrProviderContext) { it('should throttle alerts when appropriate', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -407,29 +368,27 @@ export default function ({ getService }: FtrProviderContext) { ruleId = createdRule.id; // Wait until alerts ran at least 3 times before disabling the alert and waiting for tasks to finish - await waitForNumRuleRuns({ - supertestWithoutAuth, + await alertingApi.helpers.waitForNumRuleRuns({ roleAuthc: roleAdmin, - internalReqHeader, numOfRuns: 3, ruleId, esClient, testStart, }); - await disableRule({ - supertest, + await alertingApi.helpers.disableRule({ + roleAuthc: roleAdmin, ruleId, }); - await waitForDisabled({ + await alertingApi.helpers.waiting.waitForDisabled({ esClient, ruleId, filter: testStart, }); // Ensure actions only executed once - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -440,19 +399,15 @@ export default function ({ getService }: FtrProviderContext) { it('should throttle alerts with throttled action when appropriate', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -498,29 +453,27 @@ export default function ({ getService }: FtrProviderContext) { ruleId = createdRule.id; // Wait until alerts ran at least 3 times before disabling the alert and waiting for tasks to finish - await waitForNumRuleRuns({ - supertestWithoutAuth, + await alertingApi.helpers.waitForNumRuleRuns({ roleAuthc: roleAdmin, - internalReqHeader, numOfRuns: 3, ruleId, esClient, testStart, }); - await disableRule({ - supertest, + await alertingApi.helpers.disableRule({ + roleAuthc: roleAdmin, ruleId, }); - await waitForDisabled({ + await alertingApi.helpers.waiting.waitForDisabled({ esClient, ruleId, filter: testStart, }); // Ensure actions only executed once - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -531,19 +484,15 @@ export default function ({ getService }: FtrProviderContext) { it('should reset throttle window when not firing and should not throttle when changing groups', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -614,21 +563,21 @@ export default function ({ getService }: FtrProviderContext) { ruleId = createdRule.id; // Wait for the action to index a document - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waiting.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, }); expect(resp.hits.hits.length).to.be(1); - await waitForAllTasksIdle({ + await alertingApi.helpers.waiting.waitForAllTasksIdle({ esClient, filter: testStart, }); // Update the rule to recover - await updateEsQueryRule({ - supertest, + await alertingApi.helpers.updateEsQueryRule({ + roleAuthc: roleAdmin, ruleId, updates: { name: 'never fire', @@ -645,14 +594,12 @@ export default function ({ getService }: FtrProviderContext) { }, }); - await runRule({ - supertestWithoutAuth, + await alertingApi.helpers.runRule({ roleAuthc: roleAdmin, - internalReqHeader, ruleId, }); - const eventLogResp = await waitForExecutionEventLog({ + const eventLogResp = await alertingApi.helpers.waiting.waitForExecutionEventLog({ esClient, filter: testStart, ruleId, @@ -660,19 +607,19 @@ export default function ({ getService }: FtrProviderContext) { }); expect(eventLogResp.hits.hits.length).to.be(2); - await disableRule({ - supertest, + await alertingApi.helpers.disableRule({ + roleAuthc: roleAdmin, ruleId, }); - await waitForDisabled({ + await alertingApi.helpers.waiting.waitForDisabled({ esClient, ruleId, filter: testStart, }); // Ensure only 2 actions are executed - const resp2 = await waitForDocumentInIndex({ + const resp2 = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -683,21 +630,17 @@ export default function ({ getService }: FtrProviderContext) { it(`shouldn't schedule actions when alert is muted`, async () => { const testStart = new Date(); - await createIndex({ esClient, indexName: ALERT_ACTION_INDEX }); + await alertingApi.helpers.waiting.createIndex({ esClient, indexName: ALERT_ACTION_INDEX }); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, enabled: false, consumer: 'alerts', name: 'always fire', @@ -742,41 +685,39 @@ export default function ({ getService }: FtrProviderContext) { }); ruleId = createdRule.id; - await muteRule({ - supertest, + await alertingApi.helpers.muteRule({ + roleAuthc: roleAdmin, ruleId, }); - await enableRule({ - supertest, + await alertingApi.helpers.enableRule({ + roleAuthc: roleAdmin, ruleId, }); // Wait until alerts schedule actions twice to ensure actions had a chance to skip // execution once before disabling the alert and waiting for tasks to finish - await waitForNumRuleRuns({ - supertestWithoutAuth, + await alertingApi.helpers.waitForNumRuleRuns({ roleAuthc: roleAdmin, - internalReqHeader, numOfRuns: 2, ruleId, esClient, testStart, }); - await disableRule({ - supertest, + await alertingApi.helpers.disableRule({ + roleAuthc: roleAdmin, ruleId, }); - await waitForDisabled({ + await alertingApi.helpers.waiting.waitForDisabled({ esClient, ruleId, filter: testStart, }); // Should not have executed any action - const resp2 = await getDocumentsInIndex({ + const resp2 = await alertingApi.helpers.waiting.getDocumentsInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -786,21 +727,17 @@ export default function ({ getService }: FtrProviderContext) { it(`shouldn't schedule actions when alert instance is muted`, async () => { const testStart = new Date(); - await createIndex({ esClient, indexName: ALERT_ACTION_INDEX }); + await alertingApi.helpers.waiting.createIndex({ esClient, indexName: ALERT_ACTION_INDEX }); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, enabled: false, consumer: 'alerts', name: 'always fire', @@ -845,42 +782,40 @@ export default function ({ getService }: FtrProviderContext) { }); ruleId = createdRule.id; - await muteAlert({ - supertest, + await alertingApi.helpers.muteAlert({ + roleAuthc: roleAdmin, ruleId, alertId: 'query matched', }); - await enableRule({ - supertest, + await alertingApi.helpers.enableRule({ + roleAuthc: roleAdmin, ruleId, }); // Wait until alerts schedule actions twice to ensure actions had a chance to skip // execution once before disabling the alert and waiting for tasks to finish - await waitForNumRuleRuns({ - supertestWithoutAuth, + await alertingApi.helpers.waitForNumRuleRuns({ roleAuthc: roleAdmin, - internalReqHeader, numOfRuns: 2, ruleId, esClient, testStart, }); - await disableRule({ - supertest, + await alertingApi.helpers.disableRule({ + roleAuthc: roleAdmin, ruleId, }); - await waitForDisabled({ + await alertingApi.helpers.waiting.waitForDisabled({ esClient, ruleId, filter: testStart, }); // Should not have executed any action - const resp2 = await getDocumentsInIndex({ + const resp2 = await alertingApi.helpers.waiting.getDocumentsInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -889,19 +824,15 @@ export default function ({ getService }: FtrProviderContext) { }); it(`should unmute all instances when unmuting an alert`, async () => { - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, enabled: false, consumer: 'alerts', name: 'always fire', @@ -946,29 +877,29 @@ export default function ({ getService }: FtrProviderContext) { }); ruleId = createdRule.id; - await muteAlert({ - supertest, + await alertingApi.helpers.muteAlert({ + roleAuthc: roleAdmin, ruleId, alertId: 'query matched', }); - await muteRule({ - supertest, + await alertingApi.helpers.muteRule({ + roleAuthc: roleAdmin, ruleId, }); - await unmuteRule({ - supertest, + await alertingApi.helpers.unmuteRule({ + roleAuthc: roleAdmin, ruleId, }); - await enableRule({ - supertest, + await alertingApi.helpers.enableRule({ + roleAuthc: roleAdmin, ruleId, }); // Should have one document indexed by the action - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts index 995a7ee197610..b418e9f986874 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts @@ -29,25 +29,15 @@ import { } from '@kbn/rule-data-utils'; import { omit, padStart } from 'lodash'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { createIndexConnector, createEsQueryRule } from './helpers/alerting_api_helper'; -import { - createIndex, - getDocumentsInIndex, - waitForAlertInIndex, - waitForDocumentInIndex, -} from './helpers/alerting_wait_for_helpers'; -import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; +import { RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esClient = getService('es'); const esDeleteAllIndices = getService('esDeleteAllIndices'); - - const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); + const alertingApi = getService('alertingApi'); let roleAdmin: RoleCredentials; - let internalReqHeader: InternalRequestHeader; describe('Summary actions', function () { const RULE_TYPE_ID = '.es-query'; @@ -75,7 +65,6 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { roleAdmin = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); }); afterEach(async () => { @@ -98,19 +87,15 @@ export default function ({ getService }: FtrProviderContext) { it('should schedule actions for summary of alerts per rule run', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -158,14 +143,14 @@ export default function ({ getService }: FtrProviderContext) { }); ruleId = createdRule.id; - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, }); expect(resp.hits.hits.length).to.be(1); - const resp2 = await waitForAlertInIndex({ + const resp2 = await alertingApi.helpers.waitForAlertInIndex({ esClient, filter: testStart, indexName: ALERT_INDEX, @@ -228,19 +213,15 @@ export default function ({ getService }: FtrProviderContext) { it('should filter alerts by kql', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -288,14 +269,14 @@ export default function ({ getService }: FtrProviderContext) { }); ruleId = createdRule.id; - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, }); expect(resp.hits.hits.length).to.be(1); - const resp2 = await waitForAlertInIndex({ + const resp2 = await alertingApi.helpers.waitForAlertInIndex({ esClient, filter: testStart, indexName: ALERT_INDEX, @@ -365,21 +346,17 @@ export default function ({ getService }: FtrProviderContext) { const start = `${hour}:${minutes}`; const end = `${hour}:${minutes}`; - await createIndex({ esClient, indexName: ALERT_ACTION_INDEX }); + await alertingApi.helpers.waiting.createIndex({ esClient, indexName: ALERT_ACTION_INDEX }); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -433,7 +410,7 @@ export default function ({ getService }: FtrProviderContext) { ruleId = createdRule.id; // Should not have executed any action - const resp = await getDocumentsInIndex({ + const resp = await alertingApi.helpers.waiting.getDocumentsInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -443,19 +420,15 @@ export default function ({ getService }: FtrProviderContext) { it('should schedule actions for summary of alerts on a custom interval', async () => { const testStart = new Date(); - const createdConnector = await createIndexConnector({ - supertestWithoutAuth, + const createdConnector = await alertingApi.helpers.createIndexConnector({ roleAuthc: roleAdmin, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: ALERT_ACTION_INDEX, }); connectorId = createdConnector.id; - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc: roleAdmin, - internalReqHeader, consumer: 'alerts', name: 'always fire', ruleTypeId: RULE_TYPE_ID, @@ -501,7 +474,7 @@ export default function ({ getService }: FtrProviderContext) { }); ruleId = createdRule.id; - const resp = await waitForDocumentInIndex({ + const resp = await alertingApi.helpers.waitForDocumentInIndex({ esClient, indexName: ALERT_ACTION_INDEX, ruleId, @@ -509,7 +482,7 @@ export default function ({ getService }: FtrProviderContext) { sort: 'asc', }); - const resp2 = await waitForAlertInIndex({ + const resp2 = await alertingApi.helpers.waitForAlertInIndex({ esClient, filter: testStart, indexName: ALERT_INDEX, 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 index 39edd9ba01eb9..8d627413ecbc0 100644 --- 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 @@ -12,7 +12,6 @@ */ 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) { @@ -22,7 +21,6 @@ export default function ({ getService }: FtrProviderContext) { const alertingApi = getService('alertingApi'); const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); let roleAuthc: RoleCredentials; let internalReqHeader: InternalRequestHeader; @@ -58,10 +56,8 @@ export default function ({ getService }: FtrProviderContext) { indexName: ALERT_ACTION_INDEX, }); - const createdRule = await createEsQueryRule({ - supertestWithoutAuth, + const createdRule = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, consumer: 'observability', name: 'always fire', ruleTypeId: RULE_TYPE_ID, diff --git a/x-pack/test_serverless/functional/services/index.ts b/x-pack/test_serverless/functional/services/index.ts index c63a16b4402f1..770cdbb88c97a 100644 --- a/x-pack/test_serverless/functional/services/index.ts +++ b/x-pack/test_serverless/functional/services/index.ts @@ -7,7 +7,6 @@ import { services as deploymentAgnosticFunctionalServices } from './deployment_agnostic_services'; import { services as svlSharedServices } from '../../shared/services'; - import { SvlCommonNavigationServiceProvider } from './svl_common_navigation'; import { SvlObltNavigationServiceProvider } from './svl_oblt_navigation'; import { SvlSearchNavigationServiceProvider } from './svl_search_navigation'; @@ -17,6 +16,7 @@ import { SvlCasesServiceProvider } from '../../api_integration/services/svl_case import { MachineLearningProvider } from './ml'; import { LogsSynthtraceProvider } from './log'; import { UISettingsServiceProvider } from './ui_settings'; +import { services as SvlApiIntegrationSvcs } from '../../api_integration/services'; export const services = { // deployment agnostic FTR services @@ -34,4 +34,5 @@ export const services = { uiSettings: UISettingsServiceProvider, // log services svlLogsSynthtraceClient: LogsSynthtraceProvider, + alertingApi: SvlApiIntegrationSvcs.alertingApi, }; diff --git a/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts b/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts index c2b878511a4dd..6a0d515afd232 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts @@ -7,17 +7,7 @@ import { expect } from 'expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { - createAnomalyRule as createRule, - disableRule, - enableRule, - runRule, - createIndexConnector, - snoozeRule, - createLatencyThresholdRule, - createEsQueryRule, -} from '../../../../api_integration/test_suites/common/alerting/helpers/alerting_api_helper'; -import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; +import { RoleCredentials } from '../../../../shared/services'; export default ({ getPageObject, getService }: FtrProviderContext) => { const svlCommonPage = getPageObject('svlCommonPage'); @@ -31,11 +21,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const retry = getService('retry'); const toasts = getService('toasts'); const log = getService('log'); - const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); + const alertingApi = getService('alertingApi'); let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; async function refreshRulesList() { const existsClearFilter = await testSubjects.exists('rules-list-clear-filter'); @@ -57,10 +45,13 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { numAttempts: number; }) { for (let i = 0; i < numAttempts; i++) { - await runRule({ supertestWithoutAuth, roleAuthc, internalReqHeader, ruleId }); + await alertingApi.helpers.runRule({ roleAuthc, ruleId }); await new Promise((resolve) => setTimeout(resolve, intervalMilliseconds)); - await disableRule({ supertest, ruleId }); + await alertingApi.helpers.disableRule({ + roleAuthc, + ruleId, + }); await new Promise((resolve) => setTimeout(resolve, intervalMilliseconds)); await refreshRulesList(); @@ -68,7 +59,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const rulesStatuses = result.map((item: { status: string }) => item.status); if (rulesStatuses.includes('Failed')) return; - await enableRule({ supertest, ruleId }); + await alertingApi.helpers.enableRule({ roleAuthc, ruleId }); } } @@ -84,7 +75,6 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { before(async () => { roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); await svlCommonPage.loginWithPrivilegedRole(); await svlObltNavigation.navigateToLandingPage(); await svlCommonNavigation.sidenav.clickLink({ text: 'Alerts' }); @@ -107,10 +97,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should create an ES Query Rule and display it when consumer is observability', async () => { - const esQuery = await createEsQueryRule({ - supertestWithoutAuth, + const esQuery = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, name: 'ES Query', consumer: 'observability', ruleTypeId: '.es-query', @@ -134,10 +122,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should create an ES Query rule but not display it when consumer is stackAlerts', async () => { - const esQuery = await createEsQueryRule({ - supertestWithoutAuth, + const esQuery = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, name: 'ES Query', consumer: 'stackAlerts', ruleTypeId: '.es-query', @@ -159,7 +145,10 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should create and display an APM latency rule', async () => { - const apmLatency = await createLatencyThresholdRule({ supertest, name: 'Apm latency' }); + const apmLatency = await alertingApi.helpers.createLatencyThresholdRule({ + roleAuthc, + name: 'Apm latency', + }); ruleIdList = [apmLatency.id]; await refreshRulesList(); @@ -169,16 +158,16 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should display rules in alphabetical order', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'b', }); - const rule2 = await createRule({ - supertest, + const rule2 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'c', }); - const rule3 = await createRule({ - supertest, + const rule3 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'a', }); @@ -194,8 +183,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should search for rule', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; @@ -215,13 +204,13 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should update rule list on the search clear button click', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'a', }); - const rule2 = await createRule({ - supertest, + const rule2 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'b', tags: [], }); @@ -266,8 +255,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should search for tags', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'a', tags: ['tag', 'tagtag', 'taggity tag'], }); @@ -289,8 +278,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should display an empty list when search did not return any rules', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; @@ -301,8 +290,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should disable single rule', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; @@ -329,14 +318,17 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should re-enable single rule', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'a', }); ruleIdList = [rule1.id]; - await disableRule({ supertest, ruleId: rule1.id }); + await alertingApi.helpers.disableRule({ + roleAuthc, + ruleId: rule1.id, + }); await refreshRulesList(); await svlTriggersActionsUI.searchRules(rule1.name); @@ -360,13 +352,13 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should delete single rule', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'a', }); - const rule2 = await createRule({ - supertest, + const rule2 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'b', }); @@ -392,8 +384,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should disable all selection', async () => { - const createdRule1 = await createRule({ - supertest, + const createdRule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [createdRule1.id]; @@ -422,13 +414,16 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should enable all selection', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; - await disableRule({ supertest, ruleId: rule1.id }); + await alertingApi.helpers.disableRule({ + roleAuthc, + ruleId: rule1.id, + }); await refreshRulesList(); await svlTriggersActionsUI.searchRules(rule1.name); @@ -445,8 +440,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should render percentile column and cells correctly', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; @@ -481,8 +476,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should delete all selection', async () => { - const createdRule1 = await createRule({ - supertest, + const createdRule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [createdRule1.id]; @@ -508,12 +503,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it.skip('should filter rules by the status', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); - const failedRule = await createRule({ - supertest, + const failedRule = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id, failedRule.id]; @@ -558,8 +553,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it.skip('should display total rules by status and error banner only when exists rules with status error', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); await refreshRulesList(); @@ -582,8 +577,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { ); expect(alertsErrorBannerWhenNoErrors).toHaveLength(0); - const failedRule = await createRule({ - supertest, + const failedRule = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id, failedRule.id]; @@ -617,8 +612,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it.skip('Expand error in rules table when there is rule with an error associated', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, name: 'a', }); @@ -639,8 +634,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { let expandRulesErrorLink = await find.allByCssSelector('[data-test-subj="expandRulesError"]'); expect(expandRulesErrorLink).toHaveLength(0); - const failedRule = await createRule({ - supertest, + const failedRule = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id, failedRule.id]; @@ -666,12 +661,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should filter rules by the rule type', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); - const rule2 = await createLatencyThresholdRule({ - supertest, + const rule2 = await alertingApi.helpers.createLatencyThresholdRule({ + roleAuthc, }); ruleIdList = [rule1.id, rule2.id]; @@ -730,36 +725,36 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }; // Enabled alert - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); - const disabledRule = await createRule({ - supertest, + const disabledRule = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); - await disableRule({ - supertest, + await alertingApi.helpers.disableRule({ + roleAuthc, ruleId: disabledRule.id, }); - const snoozedRule = await createRule({ - supertest, + const snoozedRule = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); - await snoozeRule({ - supertest, + await alertingApi.helpers.snoozeRule({ + roleAuthc, ruleId: snoozedRule.id, }); - const snoozedAndDisabledRule = await createRule({ - supertest, + const snoozedAndDisabledRule = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); - await snoozeRule({ - supertest, + await alertingApi.helpers.snoozeRule({ + roleAuthc, ruleId: snoozedAndDisabledRule.id, }); - await disableRule({ - supertest, + await alertingApi.helpers.disableRule({ + roleAuthc, ruleId: snoozedAndDisabledRule.id, }); @@ -801,28 +796,28 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should filter rules by the tag', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, tags: ['a'], }); - const rule2 = await createRule({ - supertest, + const rule2 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, tags: ['b'], }); - const rule3 = await createRule({ - supertest, + const rule3 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, tags: ['a', 'b'], }); - const rule4 = await createRule({ - supertest, + const rule4 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, tags: ['b', 'c'], }); - const rule5 = await createRule({ - supertest, + const rule5 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, tags: ['c'], }); @@ -864,17 +859,15 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should not prevent rules with action execution capabilities from being edited', async () => { - const action = await createIndexConnector({ - supertestWithoutAuth, + const action = await alertingApi.helpers.createIndexConnector({ roleAuthc, - internalReqHeader, name: 'Index Connector: Alerting API test', indexName: '.alerts-observability.apm.alerts-default', }); expect(action).not.toBe(undefined); - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, actions: [ { group: 'threshold_met', @@ -902,8 +895,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should allow rules to be snoozed using the right side dropdown', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; @@ -922,8 +915,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should allow rules to be snoozed indefinitely using the right side dropdown', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; @@ -942,14 +935,14 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should allow snoozed rules to be unsnoozed using the right side dropdown', async () => { - const rule1 = await createRule({ - supertest, + const rule1 = await alertingApi.helpers.createAnomalyRule({ + roleAuthc, }); ruleIdList = [rule1.id]; - await snoozeRule({ - supertest, + await alertingApi.helpers.snoozeRule({ + roleAuthc, ruleId: rule1.id, }); diff --git a/x-pack/test_serverless/functional/test_suites/search/rules/rule_details.ts b/x-pack/test_serverless/functional/test_suites/search/rules/rule_details.ts index 40d57101693bc..00363f21299de 100644 --- a/x-pack/test_serverless/functional/test_suites/search/rules/rule_details.ts +++ b/x-pack/test_serverless/functional/test_suites/search/rules/rule_details.ts @@ -7,13 +7,8 @@ import { expect } from 'expect'; import { v4 as uuidv4 } from 'uuid'; -import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; +import { RoleCredentials } from '../../../../shared/services'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { - createEsQueryRule as createRule, - createSlackConnector, - createIndexConnector, -} from '../../../../api_integration/test_suites/common/alerting/helpers/alerting_api_helper'; export enum RuleNotifyWhen { CHANGE = 'onActionGroupChange', @@ -34,6 +29,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const toasts = getService('toasts'); const comboBox = getService('comboBox'); const config = getService('config'); + const alertingApi = getService('alertingApi'); const openFirstRule = async (ruleName: string) => { await svlTriggersActionsUI.searchRules(ruleName); @@ -66,15 +62,11 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { let ruleIdList: string[]; let connectorIdList: string[]; - const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; before(async () => { roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); await svlCommonPage.loginAsViewer(); }); @@ -88,10 +80,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const RULE_TYPE_ID = '.es-query'; before(async () => { - const rule = await createRule({ - supertestWithoutAuth, + const rule = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, consumer: 'alerts', name: ruleName, ruleTypeId: RULE_TYPE_ID, @@ -261,10 +251,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const RULE_TYPE_ID = '.es-query'; before(async () => { - const rule = await createRule({ - supertestWithoutAuth, + const rule = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, consumer: 'alerts', name: ruleName, ruleTypeId: RULE_TYPE_ID, @@ -369,26 +357,20 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('should show and update deleted connectors when there are existing connectors of the same type', async () => { const testRunUuid = uuidv4(); - const connector1 = await createSlackConnector({ - supertestWithoutAuth, + const connector1 = await alertingApi.helpers.createSlackConnector({ roleAuthc, - internalReqHeader, name: `slack-${testRunUuid}-${0}`, }); - const connector2 = await createSlackConnector({ - supertestWithoutAuth, + const connector2 = await alertingApi.helpers.createSlackConnector({ roleAuthc, - internalReqHeader, name: `slack-${testRunUuid}-${1}`, }); connectorIdList = [connector2.id]; - const rule = await createRule({ - supertestWithoutAuth, + const rule = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, consumer: 'alerts', name: testRunUuid, ruleTypeId: RULE_TYPE_ID, @@ -450,18 +432,14 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('should show and update deleted connectors when there are no existing connectors of the same type', async () => { const testRunUuid = uuidv4(); - const connector = await createIndexConnector({ - supertestWithoutAuth, + const connector = await alertingApi.helpers.createIndexConnector({ roleAuthc, - internalReqHeader, name: `index-${testRunUuid}-${2}`, indexName: ALERT_ACTION_INDEX, }); - const rule = await createRule({ - supertestWithoutAuth, + const rule = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, consumer: 'alerts', name: testRunUuid, ruleTypeId: RULE_TYPE_ID, @@ -576,26 +554,20 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const testRunUuid = uuidv4(); const RULE_TYPE_ID = '.es-query'; - const connector1 = await createSlackConnector({ - supertestWithoutAuth, + const connector1 = await alertingApi.helpers.createSlackConnector({ roleAuthc, - internalReqHeader, name: `slack-${testRunUuid}-${0}`, }); - const connector2 = await createSlackConnector({ - supertestWithoutAuth, + const connector2 = await alertingApi.helpers.createSlackConnector({ roleAuthc, - internalReqHeader, name: `slack-${testRunUuid}-${1}`, }); connectorIdList = [connector1.id, connector2.id]; - const rule = await createRule({ - supertestWithoutAuth, + const rule = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, consumer: 'alerts', name: `test-rule-${testRunUuid}`, ruleTypeId: RULE_TYPE_ID, @@ -670,10 +642,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('renders a disabled rule details view in app button', async () => { - const rule = await createRule({ - supertestWithoutAuth, + const rule = await alertingApi.helpers.createEsQueryRule({ roleAuthc, - internalReqHeader, consumer: 'alerts', name: ruleName, ruleTypeId: RULE_TYPE_ID, From 591081e6c1a3dc3949fc47c7fb25664bb31d2d02 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Wed, 11 Sep 2024 11:54:10 +0100 Subject: [PATCH 02/14] Re-home alerting api under shared services such that it's available from deployment agnostic (DA). --- x-pack/test_serverless/api_integration/services/index.ts | 2 -- .../{api_integration => shared}/services/alerting_api.ts | 6 +++--- .../shared/services/deployment_agnostic_services.ts | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) rename x-pack/test_serverless/{api_integration => shared}/services/alerting_api.ts (99%) diff --git a/x-pack/test_serverless/api_integration/services/index.ts b/x-pack/test_serverless/api_integration/services/index.ts index 347fc1f68b0ca..22ce9b3bb4794 100644 --- a/x-pack/test_serverless/api_integration/services/index.ts +++ b/x-pack/test_serverless/api_integration/services/index.ts @@ -9,7 +9,6 @@ import { GenericFtrProviderContext } from '@kbn/test'; import { services as deploymentAgnosticSharedServices } from '../../shared/services/deployment_agnostic_services'; import { services as svlSharedServices } from '../../shared/services'; -import { AlertingApiProvider } from './alerting_api'; import { SamlToolsProvider } from './saml_tools'; import { SvlCasesServiceProvider } from './svl_cases'; import { SloApiProvider } from './slo_api'; @@ -35,7 +34,6 @@ export const services = { // serverless FTR services ...svlSharedServices, - alertingApi: AlertingApiProvider, samlTools: SamlToolsProvider, svlCases: SvlCasesServiceProvider, sloApi: SloApiProvider, diff --git a/x-pack/test_serverless/api_integration/services/alerting_api.ts b/x-pack/test_serverless/shared/services/alerting_api.ts similarity index 99% rename from x-pack/test_serverless/api_integration/services/alerting_api.ts rename to x-pack/test_serverless/shared/services/alerting_api.ts index 4e97ac2afb74b..fb8bcb28aa9f7 100644 --- a/x-pack/test_serverless/api_integration/services/alerting_api.ts +++ b/x-pack/test_serverless/shared/services/alerting_api.ts @@ -15,9 +15,9 @@ import type { Client } from '@elastic/elasticsearch'; import { MetricThresholdParams } from '@kbn/infra-plugin/common/alerting/metrics'; import { ThresholdParams } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { v4 as uuidv4 } from 'uuid'; -import { RoleCredentials } from '../../shared/services'; -import { SloBurnRateRuleParams } from './slo_api'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { RoleCredentials } from '.'; +import type { SloBurnRateRuleParams } from '../../api_integration/services/slo_api'; +import { FtrProviderContext } from '../../functional/ftr_provider_context'; interface CreateEsQueryRuleParams { size: number; diff --git a/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts b/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts index 97a5963bd9e3b..2272890e52eb4 100644 --- a/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts +++ b/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts @@ -8,7 +8,7 @@ import _ from 'lodash'; import { services as apiIntegrationServices } from '@kbn/test-suites-xpack/api_integration/services'; - +import { AlertingApiProvider } from './alerting_api'; /* * Some FTR services from api integration stateful tests are compatible with serverless environment * While adding a new one, make sure to verify that it works on both Kibana CI and MKI @@ -35,4 +35,5 @@ const deploymentAgnosticApiIntegrationServices = _.pick(apiIntegrationServices, export const services = { // deployment agnostic FTR services ...deploymentAgnosticApiIntegrationServices, + alertingApi: AlertingApiProvider, }; From aeafdba5bbb1a8114e0c47fab1c55ca4a9a37275 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Wed, 11 Sep 2024 12:32:40 +0100 Subject: [PATCH 03/14] drop p-retry --- .../shared/services/alerting_api.ts | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/x-pack/test_serverless/shared/services/alerting_api.ts b/x-pack/test_serverless/shared/services/alerting_api.ts index fb8bcb28aa9f7..aec745174ed90 100644 --- a/x-pack/test_serverless/shared/services/alerting_api.ts +++ b/x-pack/test_serverless/shared/services/alerting_api.ts @@ -6,7 +6,6 @@ */ import moment from 'moment'; -import pRetry from 'p-retry'; import type { AggregationsAggregate, SearchResponse, @@ -62,7 +61,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId: string; num: number; }): Promise>> { - return await pRetry( + return await retry.tryWithRetries( + 'Alerting API - waitForAlertInIndex', async () => { const response = await esClient.search({ index: indexName, @@ -87,12 +87,14 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { }, }, }); - if (response.hits.hits.length < num) { + if (response.hits.hits.length < num) throw new Error(`Only found ${response.hits.hits.length} / ${num} documents`); - } + return response; }, - { retries: 10 } + { + retryCount: 10, + } ); }, @@ -109,7 +111,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { num?: number; sort?: 'asc' | 'desc'; }): Promise { - return await pRetry( + return await retry.tryWithRetries( + 'Alerting API - waitForDocumentInIndex', async () => { const response = await esClient.search({ index: indexName, @@ -133,7 +136,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retries: 10 } + { retryCount: 10 } ); }, @@ -455,7 +458,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { testStart: Date; }) { for (let i = 0; i < numOfRuns; i++) { - await pRetry( + await retry.tryWithRetries( + 'Alerting API - waitForNumRuleRuns', async () => { await this.runRule({ roleAuthc, ruleId }); await this.waiting.waitForExecutionEventLog({ @@ -466,7 +470,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { }); await this.waiting.waitForAllTasksIdle({ esClient, filter: testStart }); }, - { retries: 10 } + { retryCount: 10 } ); } }, @@ -559,7 +563,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { num?: number; sort?: 'asc' | 'desc'; }): Promise { - return await pRetry( + return await retry.tryWithRetries( + 'Alerting API - waiting.waitForDocumentInIndex', async () => { const response = await esClient.search({ index: indexName, @@ -621,7 +626,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { esClient: Client; filter: Date; }): Promise { - return await pRetry( + return await retry.tryWithRetries( + 'Alerting API - waiting.waitForAllTasksIdle', async () => { const response = await esClient.search({ index: '.kibana_task_manager', @@ -658,7 +664,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retries: 10 } + { retryCount: 10 } ); }, @@ -673,7 +679,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId: string; num?: number; }): Promise { - return await pRetry( + return await retry.tryWithRetries( + 'Alerting API - waiting.waitForExecutionEventLog', async () => { const response = await esClient.search({ index: '.kibana-event-log*', @@ -717,7 +724,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retries: 10 } + { retryCount: 10 } ); }, @@ -742,7 +749,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { taskType: string; attempts: number; }): Promise { - return await pRetry( + return await retry.tryWithRetries( + 'Alerting API - waiting.waitForAllTasks', async () => { const response = await esClient.search({ index: '.kibana_task_manager', @@ -787,7 +795,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retries: 10 } + { retryCount: 10 } ); }, @@ -800,7 +808,8 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId: string; filter: Date; }): Promise { - return await pRetry( + return await retry.tryWithRetries( + 'Alerting API - waiting.waitForDisabled', async () => { const response = await esClient.search({ index: '.kibana_task_manager', @@ -840,7 +849,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retries: 10 } + { retryCount: 10 } ); }, }, From 8cc0b208701939d04fb93d783291253f849d2ccd Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Wed, 11 Sep 2024 12:39:24 +0100 Subject: [PATCH 04/14] whoops --- x-pack/test_serverless/shared/services/alerting_api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test_serverless/shared/services/alerting_api.ts b/x-pack/test_serverless/shared/services/alerting_api.ts index aec745174ed90..525c2757515cb 100644 --- a/x-pack/test_serverless/shared/services/alerting_api.ts +++ b/x-pack/test_serverless/shared/services/alerting_api.ts @@ -588,7 +588,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retries: 10 } + { retryCount: 10 } ); }, From ee3425933bf77138b24b6d1230904431833f8301 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Thu, 12 Sep 2024 14:36:29 +0100 Subject: [PATCH 05/14] Increase delay between retries beyond the default of 200ms... tests passing locally finally --- x-pack/test_serverless/shared/services/alerting_api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test_serverless/shared/services/alerting_api.ts b/x-pack/test_serverless/shared/services/alerting_api.ts index 525c2757515cb..2b6aefac28ad1 100644 --- a/x-pack/test_serverless/shared/services/alerting_api.ts +++ b/x-pack/test_serverless/shared/services/alerting_api.ts @@ -136,7 +136,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount: 10 } + { retryCount: 10, retryDelay: 500 } ); }, From bd8f84304d7f600eefa6874b04cfb1f7284a2f2f Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Thu, 12 Sep 2024 15:44:00 +0100 Subject: [PATCH 06/14] Expose options, add more logging --- .../test_suites/common/alerting/rules.ts | 2 + .../shared/services/alerting_api.ts | 70 ++++++++++++++----- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts index d8304ad3e5b5d..b15115496b666 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts @@ -109,6 +109,7 @@ export default function ({ getService }: FtrProviderContext) { esClient, indexName: ALERT_ACTION_INDEX, ruleId, + retryOptions: { retryDelay: 700, retryCount: 10 }, }); expect(resp.hits.hits.length).to.be(1); @@ -203,6 +204,7 @@ export default function ({ getService }: FtrProviderContext) { esClient, indexName: ALERT_ACTION_INDEX, ruleId, + retryOptions: { retryDelay: 800, retryCount: 10 }, }); expect(resp.hits.hits.length).to.be(1); diff --git a/x-pack/test_serverless/shared/services/alerting_api.ts b/x-pack/test_serverless/shared/services/alerting_api.ts index 2b6aefac28ad1..9dfde859e9e9f 100644 --- a/x-pack/test_serverless/shared/services/alerting_api.ts +++ b/x-pack/test_serverless/shared/services/alerting_api.ts @@ -36,6 +36,12 @@ interface CreateEsQueryRuleParams { termSize?: number; index?: string[]; } +const RETRY_COUNT = 10; +const RETRY_DELAY = 1000; +interface RetryOptions { + retryCount: number; + retryDelay: number; +} export function AlertingApiProvider({ getService }: FtrProviderContext) { const retry = getService('retry'); const es = getService('es'); @@ -54,15 +60,18 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { indexName, ruleId, num = 1, + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { esClient: Client; filter: Date; indexName: string; ruleId: string; num: number; + retryOptions?: RetryOptions; }): Promise>> { + const { retryCount, retryDelay } = retryOptions; return await retry.tryWithRetries( - 'Alerting API - waitForAlertInIndex', + `Alerting API - waitForAlertInIndex, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { const response = await esClient.search({ index: indexName, @@ -92,9 +101,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { return response; }, - { - retryCount: 10, - } + { retryCount, retryDelay } ); }, @@ -104,15 +111,18 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId, num = 1, sort = 'desc', + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { esClient: Client; indexName: string; ruleId: string; num?: number; sort?: 'asc' | 'desc'; + retryOptions?: RetryOptions; }): Promise { + const { retryCount, retryDelay } = retryOptions; return await retry.tryWithRetries( - 'Alerting API - waitForDocumentInIndex', + `Alerting API - waitForDocumentInIndex, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { const response = await esClient.search({ index: indexName, @@ -136,7 +146,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount: 10, retryDelay: 500 } + { retryCount, retryDelay } ); }, @@ -450,16 +460,19 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId, esClient, testStart, + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { roleAuthc: RoleCredentials; numOfRuns: number; ruleId: string; esClient: Client; testStart: Date; + retryOptions?: RetryOptions; }) { + const { retryCount, retryDelay } = retryOptions; for (let i = 0; i < numOfRuns; i++) { await retry.tryWithRetries( - 'Alerting API - waitForNumRuleRuns', + `Alerting API - waitForNumRuleRuns, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { await this.runRule({ roleAuthc, ruleId }); await this.waiting.waitForExecutionEventLog({ @@ -470,7 +483,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { }); await this.waiting.waitForAllTasksIdle({ esClient, filter: testStart }); }, - { retryCount: 10 } + { retryCount, retryDelay } ); } }, @@ -556,15 +569,20 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId, num = 1, sort = 'desc', + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { esClient: Client; indexName: string; ruleId: string; num?: number; sort?: 'asc' | 'desc'; + retryOptions?: RetryOptions; }): Promise { + const { retryCount, retryDelay } = retryOptions; return await retry.tryWithRetries( - 'Alerting API - waiting.waitForDocumentInIndex', + `Alerting API - waiting.waitForDocumentInIndex, retryOptions: ${JSON.stringify( + retryOptions + )}`, async () => { const response = await esClient.search({ index: indexName, @@ -588,7 +606,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount: 10 } + { retryCount, retryDelay } ); }, @@ -622,12 +640,17 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { async waitForAllTasksIdle({ esClient, filter, + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { esClient: Client; filter: Date; + retryOptions?: RetryOptions; }): Promise { + const { retryCount, retryDelay } = retryOptions; return await retry.tryWithRetries( - 'Alerting API - waiting.waitForAllTasksIdle', + `Alerting API - waiting.waitForAllTasksIdle, retryOptions: ${JSON.stringify( + retryOptions + )}`, async () => { const response = await esClient.search({ index: '.kibana_task_manager', @@ -664,7 +687,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount: 10 } + { retryCount, retryDelay } ); }, @@ -673,14 +696,19 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { filter, ruleId, num = 1, + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { esClient: Client; filter: Date; ruleId: string; num?: number; + retryOptions?: RetryOptions; }): Promise { + const { retryCount, retryDelay } = retryOptions; return await retry.tryWithRetries( - 'Alerting API - waiting.waitForExecutionEventLog', + `Alerting API - waiting.waitForExecutionEventLog, retryOptions: ${JSON.stringify( + retryOptions + )}`, async () => { const response = await esClient.search({ index: '.kibana-event-log*', @@ -724,7 +752,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount: 10 } + { retryCount, retryDelay } ); }, @@ -743,14 +771,17 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { filter, taskType, attempts, + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { esClient: Client; filter: Date; taskType: string; attempts: number; + retryOptions?: RetryOptions; }): Promise { + const { retryCount, retryDelay } = retryOptions; return await retry.tryWithRetries( - 'Alerting API - waiting.waitForAllTasks', + `Alerting API - waiting.waitForAllTasks, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { const response = await esClient.search({ index: '.kibana_task_manager', @@ -795,7 +826,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount: 10 } + { retryCount, retryDelay } ); }, @@ -803,13 +834,16 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { esClient, ruleId, filter, + retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }, }: { esClient: Client; ruleId: string; filter: Date; + retryOptions?: RetryOptions; }): Promise { + const { retryCount, retryDelay } = retryOptions; return await retry.tryWithRetries( - 'Alerting API - waiting.waitForDisabled', + `Alerting API - waiting.waitForDisabled, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { const response = await esClient.search({ index: '.kibana_task_manager', @@ -849,7 +883,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount: 10 } + { retryCount, retryDelay } ); }, }, From bce9f675f0a4db426b2f0b7f79a8791f518b22df Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Thu, 12 Sep 2024 16:54:31 +0100 Subject: [PATCH 07/14] bump up by using defaults --- .../api_integration/test_suites/common/alerting/rules.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts index b15115496b666..1fe1c1ca91f38 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts @@ -109,7 +109,6 @@ export default function ({ getService }: FtrProviderContext) { esClient, indexName: ALERT_ACTION_INDEX, ruleId, - retryOptions: { retryDelay: 700, retryCount: 10 }, }); expect(resp.hits.hits.length).to.be(1); From 5033fb0947965856d1ecc3ad25aecfa2c7c8235d Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Thu, 12 Sep 2024 16:58:05 +0100 Subject: [PATCH 08/14] bump up past the default, by 500ms --- .../test_suites/common/alerting/summary_actions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts index b418e9f986874..73f35e908d929 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts @@ -480,6 +480,7 @@ export default function ({ getService }: FtrProviderContext) { ruleId, num: 2, sort: 'asc', + retryOptions: { retryDelay: 1500, retryCount: 10 }, }); const resp2 = await alertingApi.helpers.waitForAlertInIndex({ From 46773024e75ff46d3b970b883129aa7b4e69c763 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Fri, 13 Sep 2024 10:02:06 +0100 Subject: [PATCH 09/14] Expose retry options and bump the options for 2 calls that failed in ci. --- .../index.ts | 2 +- .../services/retry/index.ts | 2 +- .../services/retry/retry.ts | 2 +- .../test_suites/common/alerting/rules.ts | 8 ++++ .../common/alerting/summary_actions.ts | 5 ++- .../shared/services/alerting_api.ts | 38 +++++++++---------- 6 files changed, 33 insertions(+), 24 deletions(-) diff --git a/packages/kbn-ftr-common-functional-services/index.ts b/packages/kbn-ftr-common-functional-services/index.ts index e156949f0daf9..506566216c721 100644 --- a/packages/kbn-ftr-common-functional-services/index.ts +++ b/packages/kbn-ftr-common-functional-services/index.ts @@ -14,7 +14,7 @@ import { KibanaServerProvider } from './services/kibana_server'; export { KibanaServerProvider } from './services/kibana_server'; export type KibanaServer = ProvidedType; -export { RetryService } from './services/retry'; +export { RetryService, type TryWithRetriesOptions } from './services/retry'; import { EsArchiverProvider } from './services/es_archiver'; export type EsArchiver = ProvidedType; diff --git a/packages/kbn-ftr-common-functional-services/services/retry/index.ts b/packages/kbn-ftr-common-functional-services/services/retry/index.ts index 6f42e0368364d..f96e413da2680 100644 --- a/packages/kbn-ftr-common-functional-services/services/retry/index.ts +++ b/packages/kbn-ftr-common-functional-services/services/retry/index.ts @@ -7,4 +7,4 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export { RetryService } from './retry'; +export { RetryService, type TryWithRetriesOptions } from './retry'; diff --git a/packages/kbn-ftr-common-functional-services/services/retry/retry.ts b/packages/kbn-ftr-common-functional-services/services/retry/retry.ts index 9ddd13ea583a7..614f57064512c 100644 --- a/packages/kbn-ftr-common-functional-services/services/retry/retry.ts +++ b/packages/kbn-ftr-common-functional-services/services/retry/retry.ts @@ -11,7 +11,7 @@ import { FtrService } from '../ftr_provider_context'; import { retryForSuccess } from './retry_for_success'; import { retryForTruthy } from './retry_for_truthy'; -interface TryWithRetriesOptions { +export interface TryWithRetriesOptions { retryCount: number; retryDelay?: number; timeout?: number; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts index 1fe1c1ca91f38..593c10f371f09 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts @@ -109,6 +109,10 @@ export default function ({ getService }: FtrProviderContext) { esClient, indexName: ALERT_ACTION_INDEX, ruleId, + retryOptions: { + retryCount: 12, + retryDelay: 2000, + }, }); expect(resp.hits.hits.length).to.be(1); @@ -605,6 +609,10 @@ export default function ({ getService }: FtrProviderContext) { filter: testStart, ruleId, num: 2, + retryOptions: { + retryCount: 12, + retryDelay: 2000, + }, }); expect(eventLogResp.hits.hits.length).to.be(2); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts index 73f35e908d929..ba3798f824ac1 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts @@ -480,7 +480,10 @@ export default function ({ getService }: FtrProviderContext) { ruleId, num: 2, sort: 'asc', - retryOptions: { retryDelay: 1500, retryCount: 10 }, + retryOptions: { + retryCount: 12, + retryDelay: 2000, + }, }); const resp2 = await alertingApi.helpers.waitForAlertInIndex({ diff --git a/x-pack/test_serverless/shared/services/alerting_api.ts b/x-pack/test_serverless/shared/services/alerting_api.ts index 9dfde859e9e9f..4a4afecb80f0b 100644 --- a/x-pack/test_serverless/shared/services/alerting_api.ts +++ b/x-pack/test_serverless/shared/services/alerting_api.ts @@ -14,6 +14,7 @@ import type { Client } from '@elastic/elasticsearch'; import { MetricThresholdParams } from '@kbn/infra-plugin/common/alerting/metrics'; import { ThresholdParams } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { v4 as uuidv4 } from 'uuid'; +import type { TryWithRetriesOptions } from '@kbn/ftr-common-functional-services'; import { RoleCredentials } from '.'; import type { SloBurnRateRuleParams } from '../../api_integration/services/slo_api'; import { FtrProviderContext } from '../../functional/ftr_provider_context'; @@ -38,10 +39,7 @@ interface CreateEsQueryRuleParams { } const RETRY_COUNT = 10; const RETRY_DELAY = 1000; -interface RetryOptions { - retryCount: number; - retryDelay: number; -} + export function AlertingApiProvider({ getService }: FtrProviderContext) { const retry = getService('retry'); const es = getService('es'); @@ -67,9 +65,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { indexName: string; ruleId: string; num: number; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }): Promise>> { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waitForAlertInIndex, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { @@ -118,9 +116,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId: string; num?: number; sort?: 'asc' | 'desc'; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waitForDocumentInIndex, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { @@ -467,9 +465,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId: string; esClient: Client; testStart: Date; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }) { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; for (let i = 0; i < numOfRuns; i++) { await retry.tryWithRetries( `Alerting API - waitForNumRuleRuns, retryOptions: ${JSON.stringify(retryOptions)}`, @@ -576,9 +574,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { ruleId: string; num?: number; sort?: 'asc' | 'desc'; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForDocumentInIndex, retryOptions: ${JSON.stringify( retryOptions @@ -644,9 +642,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { }: { esClient: Client; filter: Date; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForAllTasksIdle, retryOptions: ${JSON.stringify( retryOptions @@ -702,9 +700,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { filter: Date; ruleId: string; num?: number; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForExecutionEventLog, retryOptions: ${JSON.stringify( retryOptions @@ -777,9 +775,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { filter: Date; taskType: string; attempts: number; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForAllTasks, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { @@ -839,9 +837,9 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { esClient: Client; ruleId: string; filter: Date; - retryOptions?: RetryOptions; + retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount, retryDelay } = retryOptions; + const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForDisabled, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { From c7fc0f3aa2134fb9ca821940bef71128074e3e05 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Fri, 13 Sep 2024 13:19:41 +0100 Subject: [PATCH 10/14] bump to what I would presume is a high delay --- .../test_suites/common/alerting/summary_actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts index ba3798f824ac1..9d88fc864464c 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts @@ -482,7 +482,7 @@ export default function ({ getService }: FtrProviderContext) { sort: 'asc', retryOptions: { retryCount: 12, - retryDelay: 2000, + retryDelay: 5000, }, }); From b168cf1834010149b54de2005b4bb74a0252ec20 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Fri, 13 Sep 2024 13:38:26 +0100 Subject: [PATCH 11/14] Cleanup --- .../shared/services/alerting_api.ts | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/x-pack/test_serverless/shared/services/alerting_api.ts b/x-pack/test_serverless/shared/services/alerting_api.ts index 4a4afecb80f0b..afed22fbe2c9a 100644 --- a/x-pack/test_serverless/shared/services/alerting_api.ts +++ b/x-pack/test_serverless/shared/services/alerting_api.ts @@ -67,7 +67,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { num: number; retryOptions?: TryWithRetriesOptions; }): Promise>> { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waitForAlertInIndex, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { @@ -99,7 +98,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { return response; }, - { retryCount, retryDelay } + retryOptions ); }, @@ -118,7 +117,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { sort?: 'asc' | 'desc'; retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waitForDocumentInIndex, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { @@ -144,7 +142,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount, retryDelay } + retryOptions ); }, @@ -467,7 +465,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { testStart: Date; retryOptions?: TryWithRetriesOptions; }) { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; for (let i = 0; i < numOfRuns; i++) { await retry.tryWithRetries( `Alerting API - waitForNumRuleRuns, retryOptions: ${JSON.stringify(retryOptions)}`, @@ -481,7 +478,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { }); await this.waiting.waitForAllTasksIdle({ esClient, filter: testStart }); }, - { retryCount, retryDelay } + retryOptions ); } }, @@ -576,7 +573,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { sort?: 'asc' | 'desc'; retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForDocumentInIndex, retryOptions: ${JSON.stringify( retryOptions @@ -604,7 +600,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount, retryDelay } + retryOptions ); }, @@ -644,7 +640,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { filter: Date; retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForAllTasksIdle, retryOptions: ${JSON.stringify( retryOptions @@ -685,7 +680,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount, retryDelay } + retryOptions ); }, @@ -702,7 +697,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { num?: number; retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForExecutionEventLog, retryOptions: ${JSON.stringify( retryOptions @@ -750,7 +744,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount, retryDelay } + retryOptions ); }, @@ -777,7 +771,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { attempts: number; retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForAllTasks, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { @@ -824,7 +817,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount, retryDelay } + retryOptions ); }, @@ -839,7 +832,6 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { filter: Date; retryOptions?: TryWithRetriesOptions; }): Promise { - const { retryCount = RETRY_COUNT, retryDelay = RETRY_DELAY } = retryOptions; return await retry.tryWithRetries( `Alerting API - waiting.waitForDisabled, retryOptions: ${JSON.stringify(retryOptions)}`, async () => { @@ -881,7 +873,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) { } return response; }, - { retryCount, retryDelay } + retryOptions ); }, }, From 7f118614499512c012c4182eed925ca23725b17b Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Fri, 13 Sep 2024 13:55:03 +0100 Subject: [PATCH 12/14] Add more debug output to retry for success fn. --- .../services/retry/retry_for_success.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/kbn-ftr-common-functional-services/services/retry/retry_for_success.ts b/packages/kbn-ftr-common-functional-services/services/retry/retry_for_success.ts index 5401eb21286d1..921efacd88fcc 100644 --- a/packages/kbn-ftr-common-functional-services/services/retry/retry_for_success.ts +++ b/packages/kbn-ftr-common-functional-services/services/retry/retry_for_success.ts @@ -92,7 +92,7 @@ export async function retryForSuccess(log: ToolingLog, options: Options) { if (lastError && onFailureBlock) { const before = await runAttempt(onFailureBlock); if ('error' in before) { - log.debug(`--- onRetryBlock error: ${before.error.message}`); + log.debug(`--- onRetryBlock error: ${before.error.message} - Attempt #: ${attemptCounter}`); } } @@ -104,9 +104,13 @@ export async function retryForSuccess(log: ToolingLog, options: Options) { if ('error' in attempt) { if (lastError && lastError.message === attempt.error.message) { - log.debug(`--- ${methodName} failed again with the same message...`); + log.debug( + `--- ${methodName} failed again with the same message... - Attempt #: ${attemptCounter}` + ); } else { - log.debug(`--- ${methodName} error: ${attempt.error.message}`); + log.debug( + `--- ${methodName} error: ${attempt.error.message} - Attempt #: ${attemptCounter}` + ); } lastError = attempt.error; From 6889e3fa090d83111dacfc3aede5a5f725e5bf45 Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Fri, 13 Sep 2024 15:44:09 +0100 Subject: [PATCH 13/14] even higher --- .../test_suites/common/alerting/summary_actions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts index 9d88fc864464c..bac4c3dbe9acf 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts @@ -481,8 +481,8 @@ export default function ({ getService }: FtrProviderContext) { num: 2, sort: 'asc', retryOptions: { - retryCount: 12, - retryDelay: 5000, + retryCount: 20, + retryDelay: 10_000, }, }); From f859df108d6dde75001fc0059ae3515312345d2b Mon Sep 17 00:00:00 2001 From: Tre' Seymour Date: Fri, 13 Sep 2024 17:12:52 +0100 Subject: [PATCH 14/14] bump the rest up, 'high' --- .../common/alerting/summary_actions.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts index bac4c3dbe9acf..2726af585e28f 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/summary_actions.ts @@ -147,6 +147,10 @@ export default function ({ getService }: FtrProviderContext) { esClient, indexName: ALERT_ACTION_INDEX, ruleId, + retryOptions: { + retryCount: 20, + retryDelay: 15_000, + }, }); expect(resp.hits.hits.length).to.be(1); @@ -156,6 +160,10 @@ export default function ({ getService }: FtrProviderContext) { indexName: ALERT_INDEX, ruleId, num: 1, + retryOptions: { + retryCount: 20, + retryDelay: 15_000, + }, }); expect(resp2.hits.hits.length).to.be(1); @@ -273,6 +281,10 @@ export default function ({ getService }: FtrProviderContext) { esClient, indexName: ALERT_ACTION_INDEX, ruleId, + retryOptions: { + retryCount: 20, + retryDelay: 15_000, + }, }); expect(resp.hits.hits.length).to.be(1); @@ -282,6 +294,10 @@ export default function ({ getService }: FtrProviderContext) { indexName: ALERT_INDEX, ruleId, num: 1, + retryOptions: { + retryCount: 20, + retryDelay: 15_000, + }, }); expect(resp2.hits.hits.length).to.be(1); @@ -492,6 +508,10 @@ export default function ({ getService }: FtrProviderContext) { indexName: ALERT_INDEX, ruleId, num: 1, + retryOptions: { + retryCount: 20, + retryDelay: 15_000, + }, }); expect(resp2.hits.hits.length).to.be(1);