From 87d071e4e9f66dd0887de4b6e0da17e30cc3ea77 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Fri, 5 Nov 2021 14:36:18 -0600 Subject: [PATCH 1/8] [Actionable Observability] Rewrite APM registry rules for Observability --- x-pack/test/api_integration/apis/index.ts | 1 + .../__snapshots__/registry_rules.snap | 124 ++++++++ .../apis/observability/common/constants.ts | 13 + .../apis/observability/common/types.ts | 22 ++ .../apis/observability/common/users.ts | 62 ++++ .../apis/observability/index.ts | 4 +- .../lib/cleanup_target_indices.ts | 28 ++ .../apis/observability/lib/create_alert.ts | 19 ++ .../lib/create_apm_metric_index.ts | 70 +++++ .../lib/create_transaction_metric.ts | 42 +++ .../apis/observability/lib/delete_alert.ts | 43 +++ .../lib/get_alerts_target_indices.ts | 18 ++ .../apis/observability/lib/index.ts | 13 + .../lib/wait_until_next_execution.ts | 78 +++++ .../apis/observability/registry_rules.ts | 273 ++++++++++++++++++ 15 files changed, 808 insertions(+), 2 deletions(-) create mode 100644 x-pack/test/api_integration/apis/observability/__snapshots__/registry_rules.snap create mode 100644 x-pack/test/api_integration/apis/observability/common/constants.ts create mode 100644 x-pack/test/api_integration/apis/observability/common/types.ts create mode 100644 x-pack/test/api_integration/apis/observability/common/users.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/cleanup_target_indices.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/create_alert.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/create_apm_metric_index.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/create_transaction_metric.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/delete_alert.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/get_alerts_target_indices.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/index.ts create mode 100644 x-pack/test/api_integration/apis/observability/lib/wait_until_next_execution.ts create mode 100644 x-pack/test/api_integration/apis/observability/registry_rules.ts diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index c3d08ba3066927..2aed3be81c86ec 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -34,5 +34,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./searchprofiler')); loadTestFile(require.resolve('./painless_lab')); loadTestFile(require.resolve('./file_upload')); + loadTestFile(require.resolve('./observability')); }); } diff --git a/x-pack/test/api_integration/apis/observability/__snapshots__/registry_rules.snap b/x-pack/test/api_integration/apis/observability/__snapshots__/registry_rules.snap new file mode 100644 index 00000000000000..04bf1f5c495d84 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/__snapshots__/registry_rules.snap @@ -0,0 +1,124 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`apis Observability Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 1`] = ` +Object { + "event.action": Array [ + "open", + ], + "event.kind": Array [ + "signal", + ], + "kibana.alert.duration.us": Array [ + 0, + ], + "kibana.alert.evaluation.threshold": Array [ + 30, + ], + "kibana.alert.evaluation.value": Array [ + 50, + ], + "kibana.alert.instance.id": Array [ + "apm.transaction_error_rate_opbeans-go_request_ENVIRONMENT_NOT_DEFINED", + ], + "kibana.alert.reason": Array [ + "Failed transactions rate is greater than 30% (current value is 50%) for opbeans-go", + ], + "kibana.alert.rule.category": Array [ + "Failed transaction rate threshold", + ], + "kibana.alert.rule.consumer": Array [ + "apm", + ], + "kibana.alert.rule.name": Array [ + "Failed transaction rate threshold | opbeans-go", + ], + "kibana.alert.rule.producer": Array [ + "apm", + ], + "kibana.alert.rule.rule_type_id": Array [ + "apm.transaction_error_rate", + ], + "kibana.alert.status": Array [ + "active", + ], + "kibana.alert.workflow_status": Array [ + "open", + ], + "kibana.space_ids": Array [ + "default", + ], + "processor.event": Array [ + "transaction", + ], + "service.name": Array [ + "opbeans-go", + ], + "tags": Array [ + "apm", + "service.name:opbeans-go", + ], + "transaction.type": Array [ + "request", + ], +} +`; + +exports[`apis Observability Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 2`] = ` +Object { + "event.action": Array [ + "close", + ], + "event.kind": Array [ + "signal", + ], + "kibana.alert.evaluation.threshold": Array [ + 30, + ], + "kibana.alert.evaluation.value": Array [ + 50, + ], + "kibana.alert.instance.id": Array [ + "apm.transaction_error_rate_opbeans-go_request_ENVIRONMENT_NOT_DEFINED", + ], + "kibana.alert.reason": Array [ + "Failed transactions rate is greater than 30% (current value is 50%) for opbeans-go", + ], + "kibana.alert.rule.category": Array [ + "Failed transaction rate threshold", + ], + "kibana.alert.rule.consumer": Array [ + "apm", + ], + "kibana.alert.rule.name": Array [ + "Failed transaction rate threshold | opbeans-go", + ], + "kibana.alert.rule.producer": Array [ + "apm", + ], + "kibana.alert.rule.rule_type_id": Array [ + "apm.transaction_error_rate", + ], + "kibana.alert.status": Array [ + "recovered", + ], + "kibana.alert.workflow_status": Array [ + "open", + ], + "kibana.space_ids": Array [ + "default", + ], + "processor.event": Array [ + "transaction", + ], + "service.name": Array [ + "opbeans-go", + ], + "tags": Array [ + "apm", + "service.name:opbeans-go", + ], + "transaction.type": Array [ + "request", + ], +} +`; diff --git a/x-pack/test/api_integration/apis/observability/common/constants.ts b/x-pack/test/api_integration/apis/observability/common/constants.ts new file mode 100644 index 00000000000000..55b632352ffc47 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/common/constants.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const APM_METRIC_INDEX_NAME = 'apm-8.0.0-transaction'; +export const MAX_POLLS = 10; +export const BULK_INDEX_DELAY = 1000; +export const INDEXING_DELAY = 5000; +export const ALERTS_TARGET_INDICES_URL = + '/api/observability/rules/alerts/dynamic_index_pattern?namespace=default®istrationContexts=observability.apm®istrationContexts='; diff --git a/x-pack/test/api_integration/apis/observability/common/types.ts b/x-pack/test/api_integration/apis/observability/common/types.ts new file mode 100644 index 00000000000000..c601287a20bcd0 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/common/types.ts @@ -0,0 +1,22 @@ +/* + * 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 { GenericFtrProviderContext } from '@kbn/test'; +import { Alert, AlertTypeParams } from '../../../../../plugins/alerting/common'; +import { services } from '../../../services'; + +export type GetService = GenericFtrProviderContext['getService']; + +export interface AlertParams extends AlertTypeParams { + windowSize?: number; + windowUnit?: string; + threshold?: number; + serviceName?: string; + transactionType?: string; + environment?: string; +} + +export type AlertDef = Partial>; diff --git a/x-pack/test/api_integration/apis/observability/common/users.ts b/x-pack/test/api_integration/apis/observability/common/users.ts new file mode 100644 index 00000000000000..e2712aa9137dc2 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/common/users.ts @@ -0,0 +1,62 @@ +/* + * 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 { SecurityService } from 'test/common/services/security/security'; + +export enum User { + apmReadUser = 'apm_read_user', + apmWriteUser = 'apm_write_user', +} + +export const roles = { + [User.apmReadUser]: { + kibana: [ + { + base: [], + feature: { ml: ['read'] }, + spaces: ['*'], + }, + ], + }, + [User.apmWriteUser]: { + kibana: [ + { + base: [], + feature: { ml: ['all'] }, + spaces: ['*'], + }, + ], + }, +}; + +export const users = { + [User.apmReadUser]: { + roles: ['viewer', User.apmReadUser], + }, + [User.apmWriteUser]: { + roles: ['editor', User.apmWriteUser], + }, +}; + +export const TEST_PASSWORD = 'changeme'; + +export async function createUser(security: SecurityService, user: User) { + const roleDef = roles[user]; + const userDef = users[user]; + + if (!roleDef || !userDef) { + throw new Error(`No configuration found for ${user}`); + } + + await security.role.create(user, roleDef); + + await security.user.create(user, { + full_name: user, + password: TEST_PASSWORD, + roles: userDef.roles, + }); +} diff --git a/x-pack/test/api_integration/apis/observability/index.ts b/x-pack/test/api_integration/apis/observability/index.ts index 5136ff9b3d468b..8fc1abd48382ef 100644 --- a/x-pack/test/api_integration/apis/observability/index.ts +++ b/x-pack/test/api_integration/apis/observability/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('Observability specs', () => { - loadTestFile(require.resolve('./annotations')); + describe('Observability', () => { + loadTestFile(require.resolve('./registry_rules')); }); } diff --git a/x-pack/test/api_integration/apis/observability/lib/cleanup_target_indices.ts b/x-pack/test/api_integration/apis/observability/lib/cleanup_target_indices.ts new file mode 100644 index 00000000000000..799e984941ed94 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/cleanup_target_indices.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { GetService } from '../common/types'; +import { User } from '../common/users'; +import { getAlertsTargetIndices } from './get_alerts_target_indices'; + +export const cleanupTargetIndices = async (getService: GetService, user: User) => { + const es = getService('es'); + const { body: targetIndices } = await getAlertsTargetIndices(getService, user); + try { + const aliasMap = await es.indices.getAlias({ + name: targetIndices, + allow_no_indices: true, + expand_wildcards: 'open', + }); + const indices = Object.keys(aliasMap); + expect(indices.length > 0).to.be(true); + return es.indices.delete({ index: indices }); + } catch (err) { + // do nothing + } +}; diff --git a/x-pack/test/api_integration/apis/observability/lib/create_alert.ts b/x-pack/test/api_integration/apis/observability/lib/create_alert.ts new file mode 100644 index 00000000000000..6d896e6f962cee --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/create_alert.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { User, TEST_PASSWORD } from '../common/users'; +import { GetService, AlertDef } from '../common/types'; + +export const createAlert = async (getService: GetService, user: User, alertDef: AlertDef) => { + const supertest = getService('supertest'); + const { body: response, status } = await supertest + .post('/api/alerts/alert') + .auth(user, TEST_PASSWORD) + .send(alertDef) + .set('kbn-xsrf', 'foo'); + return { alert: response, status }; +}; diff --git a/x-pack/test/api_integration/apis/observability/lib/create_apm_metric_index.ts b/x-pack/test/api_integration/apis/observability/lib/create_apm_metric_index.ts new file mode 100644 index 00000000000000..607cdf3e78f8c5 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/create_apm_metric_index.ts @@ -0,0 +1,70 @@ +/* + * 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 { APM_METRIC_INDEX_NAME } from '../common/constants'; +import { GetService } from '../common/types'; + +export const createApmMetricIndex = async (getService: GetService) => { + const es = getService('es'); + return es.indices.create({ + index: APM_METRIC_INDEX_NAME, + body: { + mappings: { + dynamic: 'strict', + properties: { + event: { + properties: { + outcome: { + type: 'keyword', + }, + }, + }, + processor: { + properties: { + event: { + type: 'keyword', + }, + }, + }, + observer: { + properties: { + version_major: { + type: 'byte', + }, + }, + }, + service: { + properties: { + name: { + type: 'keyword', + }, + environment: { + type: 'keyword', + }, + }, + }, + transaction: { + properties: { + type: { + type: 'keyword', + }, + duration: { + properties: { + histogram: { + type: 'histogram', + }, + }, + }, + }, + }, + '@timestamp': { + type: 'date', + }, + }, + }, + }, + }); +}; diff --git a/x-pack/test/api_integration/apis/observability/lib/create_transaction_metric.ts b/x-pack/test/api_integration/apis/observability/lib/create_transaction_metric.ts new file mode 100644 index 00000000000000..1af7c442fd0014 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/create_transaction_metric.ts @@ -0,0 +1,42 @@ +/* + * 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 { merge } from 'lodash'; +import { INDEXING_DELAY } from '../common/constants'; + +export const createTransactionMetric = (override: Record) => { + const now = Date.now(); + const time = now - INDEXING_DELAY; + + return merge( + { + '@timestamp': new Date(time).toISOString(), + service: { + name: 'opbeans-go', + }, + event: { + outcome: 'success', + }, + transaction: { + duration: { + histogram: { + values: [1000000], + counts: [1], + }, + }, + type: 'request', + }, + processor: { + event: 'metric', + }, + observer: { + version_major: 7, + }, + }, + override + ); +}; diff --git a/x-pack/test/api_integration/apis/observability/lib/delete_alert.ts b/x-pack/test/api_integration/apis/observability/lib/delete_alert.ts new file mode 100644 index 00000000000000..8d442fcc950451 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/delete_alert.ts @@ -0,0 +1,43 @@ +/* + * 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 { APM_METRIC_INDEX_NAME } from '../common/constants'; +import { GetService } from '../common/types'; +import { User, TEST_PASSWORD } from '../common/users'; +import { getAlertsTargetIndices } from './get_alerts_target_indices'; + +export const deleteAlert = async (getService: GetService, user: User, id: string | undefined) => { + const es = getService('es'); + const supertest = getService('supertest'); + const { body: targetIndices } = await getAlertsTargetIndices(getService, user); + if (id) { + const { body, status } = await supertest + .delete(`/api/alerts/alert/${id}`) + .auth(user, TEST_PASSWORD) + .set('kbn-xsrf', 'foo'); + + if (status >= 300) { + const error = new Error('Error deleting alert'); + Object.assign(error, { response: { body, status } }); + throw error; + } + } + + await es.deleteByQuery({ + index: targetIndices[0], + body: { + query: { + match_all: {}, + }, + }, + refresh: true, + }); + + await es.indices.delete({ + index: APM_METRIC_INDEX_NAME, + }); +}; diff --git a/x-pack/test/api_integration/apis/observability/lib/get_alerts_target_indices.ts b/x-pack/test/api_integration/apis/observability/lib/get_alerts_target_indices.ts new file mode 100644 index 00000000000000..e73477ceb25dc4 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/get_alerts_target_indices.ts @@ -0,0 +1,18 @@ +/* + * 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 { GetService } from '../common/types'; +import { User, TEST_PASSWORD } from '../common/users'; +import { ALERTS_TARGET_INDICES_URL } from '../common/constants'; + +export const getAlertsTargetIndices = async (getService: GetService, user: User) => { + const supertest = getService('supertest'); + return supertest + .get(ALERTS_TARGET_INDICES_URL) + .auth(user, TEST_PASSWORD) + .send() + .set('kbn-xsrf', 'foo'); +}; diff --git a/x-pack/test/api_integration/apis/observability/lib/index.ts b/x-pack/test/api_integration/apis/observability/lib/index.ts new file mode 100644 index 00000000000000..f87dc0f354577a --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './create_alert'; +export * from './create_apm_metric_index'; +export * from './create_transaction_metric'; +export * from './get_alerts_target_indices'; +export * from './wait_until_next_execution'; +export * from './cleanup_target_indices'; diff --git a/x-pack/test/api_integration/apis/observability/lib/wait_until_next_execution.ts b/x-pack/test/api_integration/apis/observability/lib/wait_until_next_execution.ts new file mode 100644 index 00000000000000..b4050e459240c2 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/lib/wait_until_next_execution.ts @@ -0,0 +1,78 @@ +/* + * 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 { User, TEST_PASSWORD } from '../common/users'; +import { GetService } from '../common/types'; +import { getAlertsTargetIndices } from './get_alerts_target_indices'; +import { BULK_INDEX_DELAY, MAX_POLLS } from '../common/constants'; +import { Alert } from '../../../../../plugins/alerting/common'; + +export async function waitUntilNextExecution( + getService: GetService, + user: User, + alert: Alert, + intervalInSeconds: number = 1, + count: number = 0 +): Promise { + const supertest = getService('supertest'); + const es = getService('es'); + + await new Promise((resolve) => { + setTimeout(resolve, intervalInSeconds * 1000); + }); + + const { body, status } = await supertest + .get(`/api/alerts/alert/${alert.id}`) + .auth(user, TEST_PASSWORD) + .set('kbn-xsrf', 'foo'); + + const { body: targetIndices, status: targetIndicesStatus } = await getAlertsTargetIndices( + getService, + user + ); + if (targetIndices.length === 0) { + const error = new Error('Error getting alert'); + Object.assign(error, { response: { body: targetIndices, status: targetIndicesStatus } }); + throw error; + } + + if (status >= 300) { + const error = new Error('Error getting alert'); + Object.assign(error, { response: { body, status } }); + throw error; + } + + const nextAlert = body as Alert; + + if (nextAlert.executionStatus.lastExecutionDate !== alert.executionStatus.lastExecutionDate) { + await new Promise((resolve) => { + setTimeout(resolve, BULK_INDEX_DELAY); + }); + + /** + * When calling refresh on an index pattern .alerts-observability.apm.alerts* (as was originally the hard-coded string in this test) + * The response from Elasticsearch is a 200, even if no indices which match that index pattern have been created. + * When calling refresh on a concrete index alias .alerts-observability.apm.alerts-default for instance, + * we receive a 404 error index_not_found_exception when no indices have been created which match that alias (obviously). + * Since we are receiving a concrete index alias from the observability api instead of a kibana index pattern + * and we understand / expect that this index does not exist at certain points of the test, we can try-catch at certain points without caring if the call fails. + * There are points in the code where we do want to ensure we get the appropriate error message back + */ + try { + await es.indices.refresh({ + index: targetIndices[0], + }); + // eslint-disable-next-line no-empty + } catch (exc) {} + return nextAlert; + } + + if (count >= MAX_POLLS) { + throw new Error('Maximum number of polls exceeded'); + } + + return waitUntilNextExecution(getService, user, alert, intervalInSeconds, count + 1); +} diff --git a/x-pack/test/api_integration/apis/observability/registry_rules.ts b/x-pack/test/api_integration/apis/observability/registry_rules.ts new file mode 100644 index 00000000000000..a3c4d7a484fd32 --- /dev/null +++ b/x-pack/test/api_integration/apis/observability/registry_rules.ts @@ -0,0 +1,273 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + EVENT_KIND, + VERSION, +} from '@kbn/rule-data-utils'; +import { omit } from 'lodash'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { createUser, User } from './common/users'; +import { + getAlertsTargetIndices, + createApmMetricIndex, + createAlert, + waitUntilNextExecution, + createTransactionMetric, + cleanupTargetIndices, +} from './lib'; +import { AlertDef, AlertParams } from './common/types'; +import { Alert } from '../../../../plugins/alerting/common'; +import { deleteAlert } from './lib/delete_alert'; +import { APM_METRIC_INDEX_NAME } from './common/constants'; + +export default function registryRulesApiTest({ getService }: FtrProviderContext) { + const es = getService('es'); + const security = getService('security'); + + describe('Rule Registry API', () => { + before(async () => { + await createUser(security, User.apmReadUser); + await createUser(security, User.apmWriteUser); + }); + + describe('with write permissions', () => { + it('does not bootstrap indices on plugin startup', async () => { + const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmWriteUser); + try { + const res = await es.indices.get({ + index: targetIndices[0], + expand_wildcards: 'open', + allow_no_indices: true, + }); + expect(res).to.be.empty(); + } catch (exc) { + expect(exc.statusCode).to.eql(404); + } + }); + + describe('when creating a rule', () => { + let createResponse: { + alert: Alert; + status: number; + }; + before(async () => { + await createApmMetricIndex(getService); + const alertDef: AlertDef = { + params: { + threshold: 30, + windowSize: 5, + windowUnit: 'm', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + serviceName: 'opbeans-go', + }, + consumer: 'apm', + alertTypeId: 'apm.transaction_error_rate', + schedule: { interval: '5s' }, + actions: [], + tags: ['apm', 'service.name:opbeans-go'], + notifyWhen: 'onActionGroupChange', + name: 'Failed transaction rate threshold | opbeans-go', + }; + createResponse = await createAlert(getService, User.apmWriteUser, alertDef); + }); + after(async () => { + await deleteAlert(getService, User.apmWriteUser, createResponse.alert.id); + await cleanupTargetIndices(getService, User.apmWriteUser); + }); + + it('writes alerts data to the alert indices', async () => { + expect(createResponse.status).to.be.below(299); + + expect(createResponse.alert).not.to.be(undefined); + let alert = await waitUntilNextExecution( + getService, + User.apmWriteUser, + createResponse.alert + ); + + const { body: targetIndices } = await getAlertsTargetIndices( + getService, + User.apmWriteUser + ); + + try { + const res = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + }, + }); + expect(res).to.be.empty(); + } catch (exc) { + expect(exc.message).contain('index_not_found_exception'); + } + + await es.index({ + index: APM_METRIC_INDEX_NAME, + body: createTransactionMetric({ + event: { + outcome: 'success', + }, + }), + refresh: true, + }); + + alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + + try { + const res = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + }, + }); + expect(res).to.be.empty(); + } catch (exc) { + expect(exc.message).contain('index_not_found_exception'); + } + + await es.index({ + index: APM_METRIC_INDEX_NAME, + body: createTransactionMetric({ + event: { + outcome: 'failure', + }, + }), + refresh: true, + }); + + alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + + const afterViolatingDataResponse = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], + }, + }); + + expect(afterViolatingDataResponse.hits.hits.length).to.be(1); + + const alertEvent = afterViolatingDataResponse.hits.hits[0].fields as Record; + + const exclude = ['@timestamp', ALERT_START, ALERT_UUID, ALERT_RULE_UUID, VERSION]; + + const toCompare = omit(alertEvent, exclude); + + expectSnapshot(toCompare).toMatch(); + + await es.bulk({ + index: APM_METRIC_INDEX_NAME, + body: [ + { index: {} }, + createTransactionMetric({ + event: { + outcome: 'success', + }, + }), + { index: {} }, + createTransactionMetric({ + event: { + outcome: 'success', + }, + }), + ], + refresh: true, + }); + + alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + + const afterRecoveryResponse = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], + }, + }); + + expect(afterRecoveryResponse.hits.hits.length).to.be(1); + + const recoveredAlertEvent = afterRecoveryResponse.hits.hits[0].fields as Record< + string, + any + >; + + expect(recoveredAlertEvent[ALERT_STATUS]?.[0]).to.eql('recovered'); + expect(recoveredAlertEvent[ALERT_DURATION]?.[0]).to.be.greaterThan(0); + expect(new Date(recoveredAlertEvent[ALERT_END]?.[0]).getTime()).to.be.greaterThan(0); + + expectSnapshot( + omit(recoveredAlertEvent, exclude.concat([ALERT_DURATION, ALERT_END])) + ).toMatch(); + }); + }); + }); + + describe('with read permissions', () => { + it('does not bootstrap the apm rule indices', async () => { + const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmReadUser); + const errorOrUndefined = await es.indices + .get({ + index: targetIndices[0], + expand_wildcards: 'open', + allow_no_indices: false, + }) + .then(() => {}) + .catch((error) => { + return error.toString(); + }); + + expect(errorOrUndefined).not.to.be(undefined); + + expect(errorOrUndefined).to.contain('index_not_found_exception'); + }); + }); + }); +} From 9142ea8831205bf442a04dcfc9df76f0a55f7931 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Mon, 8 Nov 2021 08:40:29 -0700 Subject: [PATCH 2/8] removing apm's rule registry test --- .../tests/alerts/rule_registry.spec.ts | 576 ------------------ 1 file changed, 576 deletions(-) delete mode 100644 x-pack/test/apm_api_integration/tests/alerts/rule_registry.spec.ts diff --git a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/rule_registry.spec.ts deleted file mode 100644 index efa8aa3ace9dc7..00000000000000 --- a/x-pack/test/apm_api_integration/tests/alerts/rule_registry.spec.ts +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { - ALERT_DURATION, - ALERT_END, - ALERT_RULE_UUID, - ALERT_START, - ALERT_STATUS, - ALERT_UUID, - EVENT_KIND, - VERSION, -} from '@kbn/rule-data-utils'; -import { merge, omit } from 'lodash'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; - -interface Alert { - schedule: { - interval: string; - }; - updatedAt: string; - executionStatus: { - lastExecutionDate: string; - status: string; - }; - updatedBy: string; - id: string; - params: Record; - scheduledTaskId: string; -} - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const supertest = getService('legacySupertestAsApmWriteUser'); - const es = getService('es'); - - const MAX_POLLS = 10; - const BULK_INDEX_DELAY = 1000; - const INDEXING_DELAY = 5000; - - const getAlertsTargetIndicesUrl = - '/api/observability/rules/alerts/dynamic_index_pattern?namespace=default®istrationContexts=observability.apm®istrationContexts='; - - const getAlertsTargetIndices = async () => - supertest.get(getAlertsTargetIndicesUrl).send().set('kbn-xsrf', 'foo'); - const APM_METRIC_INDEX_NAME = 'apm-8.0.0-transaction'; - - const createTransactionMetric = (override: Record) => { - const now = Date.now(); - - const time = now - INDEXING_DELAY; - - return merge( - { - '@timestamp': new Date(time).toISOString(), - service: { - name: 'opbeans-go', - }, - event: { - outcome: 'success', - }, - transaction: { - duration: { - histogram: { - values: [1000000], - counts: [1], - }, - }, - type: 'request', - }, - processor: { - event: 'metric', - }, - observer: { - version_major: 7, - }, - }, - override - ); - }; - - async function waitUntilNextExecution( - alert: Alert, - intervalInSeconds: number = 1, - count: number = 0 - ): Promise { - await new Promise((resolve) => { - setTimeout(resolve, intervalInSeconds * 1000); - }); - - const { body, status } = await supertest - .get(`/api/alerts/alert/${alert.id}`) - .set('kbn-xsrf', 'foo'); - - const { body: targetIndices, status: targetIndicesStatus } = await getAlertsTargetIndices(); - if (targetIndices.length === 0) { - const error = new Error('Error getting alert'); - Object.assign(error, { response: { body: targetIndices, status: targetIndicesStatus } }); - throw error; - } - - if (status >= 300) { - const error = new Error('Error getting alert'); - Object.assign(error, { response: { body, status } }); - throw error; - } - - const nextAlert = body as Alert; - - if (nextAlert.executionStatus.lastExecutionDate !== alert.executionStatus.lastExecutionDate) { - await new Promise((resolve) => { - setTimeout(resolve, BULK_INDEX_DELAY); - }); - - /** - * When calling refresh on an index pattern .alerts-observability.apm.alerts* (as was originally the hard-coded string in this test) - * The response from Elasticsearch is a 200, even if no indices which match that index pattern have been created. - * When calling refresh on a concrete index alias .alerts-observability.apm.alerts-default for instance, - * we receive a 404 error index_not_found_exception when no indices have been created which match that alias (obviously). - * Since we are receiving a concrete index alias from the observability api instead of a kibana index pattern - * and we understand / expect that this index does not exist at certain points of the test, we can try-catch at certain points without caring if the call fails. - * There are points in the code where we do want to ensure we get the appropriate error message back - */ - try { - await es.indices.refresh({ - index: targetIndices[0], - }); - // eslint-disable-next-line no-empty - } catch (exc) {} - return nextAlert; - } - - if (count >= MAX_POLLS) { - throw new Error('Maximum number of polls exceeded'); - } - - return waitUntilNextExecution(alert, intervalInSeconds, count + 1); - } - - registry.when('Rule registry with write enabled', { config: 'rules', archives: [] }, () => { - it('does not bootstrap indices on plugin startup', async () => { - const { body: targetIndices } = await getAlertsTargetIndices(); - try { - const res = await es.indices.get({ - index: targetIndices[0], - expand_wildcards: 'open', - allow_no_indices: true, - }); - expect(res).to.be.empty(); - } catch (exc) { - expect(exc.statusCode).to.eql(404); - } - }); - - describe('when creating a rule', () => { - let createResponse: { - alert: Alert; - status: number; - }; - - before(async () => { - await es.indices.create({ - index: APM_METRIC_INDEX_NAME, - body: { - mappings: { - dynamic: 'strict', - properties: { - event: { - properties: { - outcome: { - type: 'keyword', - }, - }, - }, - processor: { - properties: { - event: { - type: 'keyword', - }, - }, - }, - observer: { - properties: { - version_major: { - type: 'byte', - }, - }, - }, - service: { - properties: { - name: { - type: 'keyword', - }, - environment: { - type: 'keyword', - }, - }, - }, - transaction: { - properties: { - type: { - type: 'keyword', - }, - duration: { - properties: { - histogram: { - type: 'histogram', - }, - }, - }, - }, - }, - '@timestamp': { - type: 'date', - }, - }, - }, - }, - }); - - const body = { - params: { - threshold: 30, - windowSize: 5, - windowUnit: 'm', - transactionType: 'request', - environment: 'ENVIRONMENT_ALL', - serviceName: 'opbeans-go', - }, - consumer: 'apm', - alertTypeId: 'apm.transaction_error_rate', - schedule: { interval: '5s' }, - actions: [], - tags: ['apm', 'service.name:opbeans-go'], - notifyWhen: 'onActionGroupChange', - name: 'Failed transaction rate threshold | opbeans-go', - }; - - const { body: response, status } = await supertest - .post('/api/alerts/alert') - .send(body) - .set('kbn-xsrf', 'foo'); - - createResponse = { - alert: response, - status, - }; - }); - - after(async () => { - const { body: targetIndices } = await getAlertsTargetIndices(); - if (createResponse.alert) { - const { body, status } = await supertest - .delete(`/api/alerts/alert/${createResponse.alert.id}`) - .set('kbn-xsrf', 'foo'); - - if (status >= 300) { - const error = new Error('Error deleting alert'); - Object.assign(error, { response: { body, status } }); - throw error; - } - } - - await es.deleteByQuery({ - index: targetIndices[0], - body: { - query: { - match_all: {}, - }, - }, - refresh: true, - }); - - await es.indices.delete({ - index: APM_METRIC_INDEX_NAME, - }); - }); - - it('writes alerts data to the alert indices', async () => { - expect(createResponse.status).to.be.below(299); - - expect(createResponse.alert).not.to.be(undefined); - let alert = await waitUntilNextExecution(createResponse.alert); - - const { body: targetIndices } = await getAlertsTargetIndices(); - - try { - const res = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - }, - }); - expect(res).to.be.empty(); - } catch (exc) { - expect(exc.message).contain('index_not_found_exception'); - } - - await es.index({ - index: APM_METRIC_INDEX_NAME, - body: createTransactionMetric({ - event: { - outcome: 'success', - }, - }), - refresh: true, - }); - - alert = await waitUntilNextExecution(alert); - - try { - const res = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - }, - }); - expect(res).to.be.empty(); - } catch (exc) { - expect(exc.message).contain('index_not_found_exception'); - } - - await es.index({ - index: APM_METRIC_INDEX_NAME, - body: createTransactionMetric({ - event: { - outcome: 'failure', - }, - }), - refresh: true, - }); - - alert = await waitUntilNextExecution(alert); - - const afterViolatingDataResponse = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - _source: false, - fields: [{ field: '*', include_unmapped: true }], - }, - }); - - expect(afterViolatingDataResponse.hits.hits.length).to.be(1); - - const alertEvent = afterViolatingDataResponse.hits.hits[0].fields as Record; - - const exclude = ['@timestamp', ALERT_START, ALERT_UUID, ALERT_RULE_UUID, VERSION]; - - const toCompare = omit(alertEvent, exclude); - - expectSnapshot(toCompare).toMatchInline(` - Object { - "event.action": Array [ - "open", - ], - "event.kind": Array [ - "signal", - ], - "kibana.alert.duration.us": Array [ - 0, - ], - "kibana.alert.evaluation.threshold": Array [ - 30, - ], - "kibana.alert.evaluation.value": Array [ - 50, - ], - "kibana.alert.instance.id": Array [ - "apm.transaction_error_rate_opbeans-go_request_ENVIRONMENT_NOT_DEFINED", - ], - "kibana.alert.reason": Array [ - "Failed transactions rate is greater than 30% (current value is 50%) for opbeans-go", - ], - "kibana.alert.rule.category": Array [ - "Failed transaction rate threshold", - ], - "kibana.alert.rule.consumer": Array [ - "apm", - ], - "kibana.alert.rule.name": Array [ - "Failed transaction rate threshold | opbeans-go", - ], - "kibana.alert.rule.producer": Array [ - "apm", - ], - "kibana.alert.rule.rule_type_id": Array [ - "apm.transaction_error_rate", - ], - "kibana.alert.status": Array [ - "active", - ], - "kibana.alert.workflow_status": Array [ - "open", - ], - "kibana.space_ids": Array [ - "default", - ], - "processor.event": Array [ - "transaction", - ], - "service.name": Array [ - "opbeans-go", - ], - "tags": Array [ - "apm", - "service.name:opbeans-go", - ], - "transaction.type": Array [ - "request", - ], - } - `); - - await es.bulk({ - index: APM_METRIC_INDEX_NAME, - body: [ - { index: {} }, - createTransactionMetric({ - event: { - outcome: 'success', - }, - }), - { index: {} }, - createTransactionMetric({ - event: { - outcome: 'success', - }, - }), - ], - refresh: true, - }); - - alert = await waitUntilNextExecution(alert); - - const afterRecoveryResponse = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - _source: false, - fields: [{ field: '*', include_unmapped: true }], - }, - }); - - expect(afterRecoveryResponse.hits.hits.length).to.be(1); - - const recoveredAlertEvent = afterRecoveryResponse.hits.hits[0].fields as Record< - string, - any - >; - - expect(recoveredAlertEvent[ALERT_STATUS]?.[0]).to.eql('recovered'); - expect(recoveredAlertEvent[ALERT_DURATION]?.[0]).to.be.greaterThan(0); - expect(new Date(recoveredAlertEvent[ALERT_END]?.[0]).getTime()).to.be.greaterThan(0); - - expectSnapshot(omit(recoveredAlertEvent, exclude.concat([ALERT_DURATION, ALERT_END]))) - .toMatchInline(` - Object { - "event.action": Array [ - "close", - ], - "event.kind": Array [ - "signal", - ], - "kibana.alert.evaluation.threshold": Array [ - 30, - ], - "kibana.alert.evaluation.value": Array [ - 50, - ], - "kibana.alert.instance.id": Array [ - "apm.transaction_error_rate_opbeans-go_request_ENVIRONMENT_NOT_DEFINED", - ], - "kibana.alert.reason": Array [ - "Failed transactions rate is greater than 30% (current value is 50%) for opbeans-go", - ], - "kibana.alert.rule.category": Array [ - "Failed transaction rate threshold", - ], - "kibana.alert.rule.consumer": Array [ - "apm", - ], - "kibana.alert.rule.name": Array [ - "Failed transaction rate threshold | opbeans-go", - ], - "kibana.alert.rule.producer": Array [ - "apm", - ], - "kibana.alert.rule.rule_type_id": Array [ - "apm.transaction_error_rate", - ], - "kibana.alert.status": Array [ - "recovered", - ], - "kibana.alert.workflow_status": Array [ - "open", - ], - "kibana.space_ids": Array [ - "default", - ], - "processor.event": Array [ - "transaction", - ], - "service.name": Array [ - "opbeans-go", - ], - "tags": Array [ - "apm", - "service.name:opbeans-go", - ], - "transaction.type": Array [ - "request", - ], - } - `); - }); - }); - }); - - registry.when('Rule registry with write not enabled', { config: 'basic', archives: [] }, () => { - it('does not bootstrap the apm rule indices', async () => { - const { body: targetIndices } = await getAlertsTargetIndices(); - const errorOrUndefined = await es.indices - .get({ - index: targetIndices[0], - expand_wildcards: 'open', - allow_no_indices: false, - }) - .then(() => {}) - .catch((error) => { - return error.toString(); - }); - - expect(errorOrUndefined).not.to.be(undefined); - - expect(errorOrUndefined).to.contain('index_not_found_exception'); - }); - }); -} From b75302356810345cdd0ca0f90d5215534fea723d Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Mon, 8 Nov 2021 15:05:00 -0700 Subject: [PATCH 3/8] Moving everything under observability_api_integration --- .../apis/observability/index.ts | 4 +- .../apis/observability/registry_rules.ts | 4 +- .../basic/tests/index.ts | 1 + .../basic/tests/registry_rules.ts | 43 +++ .../common/constants.ts | 0 .../common/types.ts | 4 +- .../common/users.ts | 0 .../lib/cleanup_target_indices.ts | 0 .../lib/create_alert.ts | 0 .../lib/create_apm_metric_index.ts | 0 .../lib/create_transaction_metric.ts | 0 .../lib/delete_alert.ts | 0 .../lib/get_alerts_target_indices.ts | 0 .../lib/index.ts | 1 + .../lib/wait_until_next_execution.ts | 2 +- .../tests}/__snapshots__/registry_rules.snap | 4 +- .../trial/tests/index.ts | 1 + .../trial/tests/registry_rules.ts | 273 ++++++++++++++++++ 18 files changed, 327 insertions(+), 10 deletions(-) create mode 100644 x-pack/test/observability_api_integration/basic/tests/registry_rules.ts rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/common/constants.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/common/types.ts (83%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/common/users.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/cleanup_target_indices.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/create_alert.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/create_apm_metric_index.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/create_transaction_metric.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/delete_alert.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/get_alerts_target_indices.ts (100%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/index.ts (94%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration}/lib/wait_until_next_execution.ts (97%) rename x-pack/test/{api_integration/apis/observability => observability_api_integration/trial/tests}/__snapshots__/registry_rules.snap (89%) create mode 100644 x-pack/test/observability_api_integration/trial/tests/registry_rules.ts diff --git a/x-pack/test/api_integration/apis/observability/index.ts b/x-pack/test/api_integration/apis/observability/index.ts index 8fc1abd48382ef..354a53f2999906 100644 --- a/x-pack/test/api_integration/apis/observability/index.ts +++ b/x-pack/test/api_integration/apis/observability/index.ts @@ -8,7 +8,5 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('Observability', () => { - loadTestFile(require.resolve('./registry_rules')); - }); + describe('Observability', () => {}); } diff --git a/x-pack/test/api_integration/apis/observability/registry_rules.ts b/x-pack/test/api_integration/apis/observability/registry_rules.ts index a3c4d7a484fd32..22ac50105628ac 100644 --- a/x-pack/test/api_integration/apis/observability/registry_rules.ts +++ b/x-pack/test/api_integration/apis/observability/registry_rules.ts @@ -26,10 +26,10 @@ import { waitUntilNextExecution, createTransactionMetric, cleanupTargetIndices, -} from './lib'; +} from '../../../observability_api_integration/lib'; import { AlertDef, AlertParams } from './common/types'; import { Alert } from '../../../../plugins/alerting/common'; -import { deleteAlert } from './lib/delete_alert'; +import { deleteAlert } from '../../../observability_api_integration/lib/delete_alert'; import { APM_METRIC_INDEX_NAME } from './common/constants'; export default function registryRulesApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/observability_api_integration/basic/tests/index.ts b/x-pack/test/observability_api_integration/basic/tests/index.ts index c62cf4be0d7c7a..10a5e83378b7d8 100644 --- a/x-pack/test/observability_api_integration/basic/tests/index.ts +++ b/x-pack/test/observability_api_integration/basic/tests/index.ts @@ -12,5 +12,6 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr describe('Observability specs (basic)', function () { this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); + loadTestFile(require.resolve('./registry_rules')); }); } diff --git a/x-pack/test/observability_api_integration/basic/tests/registry_rules.ts b/x-pack/test/observability_api_integration/basic/tests/registry_rules.ts new file mode 100644 index 00000000000000..380163753c57d5 --- /dev/null +++ b/x-pack/test/observability_api_integration/basic/tests/registry_rules.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { createUser, User } from '../../common/users'; +import { getAlertsTargetIndices } from '../../lib'; + +// eslint-disable-next-line import/no-default-export +export default function registryRulesApiTest({ getService }: FtrProviderContext) { + const es = getService('es'); + const security = getService('security'); + + describe('Rule Registry API', () => { + before(async () => { + await createUser(security, User.apmReadUser); + }); + + describe('with read permissions', () => { + it('does not bootstrap the apm rule indices', async () => { + const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmReadUser); + const errorOrUndefined = await es.indices + .get({ + index: targetIndices[0], + expand_wildcards: 'open', + allow_no_indices: false, + }) + .then(() => {}) + .catch((error) => { + return error.toString(); + }); + + expect(errorOrUndefined).not.to.be(undefined); + + expect(errorOrUndefined).to.contain('index_not_found_exception'); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/observability/common/constants.ts b/x-pack/test/observability_api_integration/common/constants.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/common/constants.ts rename to x-pack/test/observability_api_integration/common/constants.ts diff --git a/x-pack/test/api_integration/apis/observability/common/types.ts b/x-pack/test/observability_api_integration/common/types.ts similarity index 83% rename from x-pack/test/api_integration/apis/observability/common/types.ts rename to x-pack/test/observability_api_integration/common/types.ts index c601287a20bcd0..8b9a569f4ed26e 100644 --- a/x-pack/test/api_integration/apis/observability/common/types.ts +++ b/x-pack/test/observability_api_integration/common/types.ts @@ -5,8 +5,8 @@ * 2.0. */ import { GenericFtrProviderContext } from '@kbn/test'; -import { Alert, AlertTypeParams } from '../../../../../plugins/alerting/common'; -import { services } from '../../../services'; +import { Alert, AlertTypeParams } from '../../../plugins/alerting/common'; +import { services } from '../../api_integration/services'; export type GetService = GenericFtrProviderContext['getService']; diff --git a/x-pack/test/api_integration/apis/observability/common/users.ts b/x-pack/test/observability_api_integration/common/users.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/common/users.ts rename to x-pack/test/observability_api_integration/common/users.ts diff --git a/x-pack/test/api_integration/apis/observability/lib/cleanup_target_indices.ts b/x-pack/test/observability_api_integration/lib/cleanup_target_indices.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/lib/cleanup_target_indices.ts rename to x-pack/test/observability_api_integration/lib/cleanup_target_indices.ts diff --git a/x-pack/test/api_integration/apis/observability/lib/create_alert.ts b/x-pack/test/observability_api_integration/lib/create_alert.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/lib/create_alert.ts rename to x-pack/test/observability_api_integration/lib/create_alert.ts diff --git a/x-pack/test/api_integration/apis/observability/lib/create_apm_metric_index.ts b/x-pack/test/observability_api_integration/lib/create_apm_metric_index.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/lib/create_apm_metric_index.ts rename to x-pack/test/observability_api_integration/lib/create_apm_metric_index.ts diff --git a/x-pack/test/api_integration/apis/observability/lib/create_transaction_metric.ts b/x-pack/test/observability_api_integration/lib/create_transaction_metric.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/lib/create_transaction_metric.ts rename to x-pack/test/observability_api_integration/lib/create_transaction_metric.ts diff --git a/x-pack/test/api_integration/apis/observability/lib/delete_alert.ts b/x-pack/test/observability_api_integration/lib/delete_alert.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/lib/delete_alert.ts rename to x-pack/test/observability_api_integration/lib/delete_alert.ts diff --git a/x-pack/test/api_integration/apis/observability/lib/get_alerts_target_indices.ts b/x-pack/test/observability_api_integration/lib/get_alerts_target_indices.ts similarity index 100% rename from x-pack/test/api_integration/apis/observability/lib/get_alerts_target_indices.ts rename to x-pack/test/observability_api_integration/lib/get_alerts_target_indices.ts diff --git a/x-pack/test/api_integration/apis/observability/lib/index.ts b/x-pack/test/observability_api_integration/lib/index.ts similarity index 94% rename from x-pack/test/api_integration/apis/observability/lib/index.ts rename to x-pack/test/observability_api_integration/lib/index.ts index f87dc0f354577a..695f71021d5ab9 100644 --- a/x-pack/test/api_integration/apis/observability/lib/index.ts +++ b/x-pack/test/observability_api_integration/lib/index.ts @@ -11,3 +11,4 @@ export * from './create_transaction_metric'; export * from './get_alerts_target_indices'; export * from './wait_until_next_execution'; export * from './cleanup_target_indices'; +export * from './delete_alert'; diff --git a/x-pack/test/api_integration/apis/observability/lib/wait_until_next_execution.ts b/x-pack/test/observability_api_integration/lib/wait_until_next_execution.ts similarity index 97% rename from x-pack/test/api_integration/apis/observability/lib/wait_until_next_execution.ts rename to x-pack/test/observability_api_integration/lib/wait_until_next_execution.ts index b4050e459240c2..4dd9380aa4a12f 100644 --- a/x-pack/test/api_integration/apis/observability/lib/wait_until_next_execution.ts +++ b/x-pack/test/observability_api_integration/lib/wait_until_next_execution.ts @@ -8,7 +8,7 @@ import { User, TEST_PASSWORD } from '../common/users'; import { GetService } from '../common/types'; import { getAlertsTargetIndices } from './get_alerts_target_indices'; import { BULK_INDEX_DELAY, MAX_POLLS } from '../common/constants'; -import { Alert } from '../../../../../plugins/alerting/common'; +import { Alert } from '../../../plugins/alerting/common'; export async function waitUntilNextExecution( getService: GetService, diff --git a/x-pack/test/api_integration/apis/observability/__snapshots__/registry_rules.snap b/x-pack/test/observability_api_integration/trial/tests/__snapshots__/registry_rules.snap similarity index 89% rename from x-pack/test/api_integration/apis/observability/__snapshots__/registry_rules.snap rename to x-pack/test/observability_api_integration/trial/tests/__snapshots__/registry_rules.snap index 04bf1f5c495d84..b990fc3a744038 100644 --- a/x-pack/test/api_integration/apis/observability/__snapshots__/registry_rules.snap +++ b/x-pack/test/observability_api_integration/trial/tests/__snapshots__/registry_rules.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`apis Observability Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 1`] = ` +exports[`Observability specs (trial) Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 1`] = ` Object { "event.action": Array [ "open", @@ -63,7 +63,7 @@ Object { } `; -exports[`apis Observability Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 2`] = ` +exports[`Observability specs (trial) Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 2`] = ` Object { "event.action": Array [ "close", diff --git a/x-pack/test/observability_api_integration/trial/tests/index.ts b/x-pack/test/observability_api_integration/trial/tests/index.ts index 037cf48275de28..f58b14104d8892 100644 --- a/x-pack/test/observability_api_integration/trial/tests/index.ts +++ b/x-pack/test/observability_api_integration/trial/tests/index.ts @@ -12,5 +12,6 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont describe('Observability specs (trial)', function () { this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); + loadTestFile(require.resolve('./registry_rules')); }); } diff --git a/x-pack/test/observability_api_integration/trial/tests/registry_rules.ts b/x-pack/test/observability_api_integration/trial/tests/registry_rules.ts new file mode 100644 index 00000000000000..db2734f6f4df86 --- /dev/null +++ b/x-pack/test/observability_api_integration/trial/tests/registry_rules.ts @@ -0,0 +1,273 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { + ALERT_DURATION, + ALERT_END, + ALERT_RULE_UUID, + ALERT_START, + ALERT_STATUS, + ALERT_UUID, + EVENT_KIND, + VERSION, +} from '@kbn/rule-data-utils'; +import { omit } from 'lodash'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { createUser, User } from '../../common/users'; +import { + getAlertsTargetIndices, + createApmMetricIndex, + createAlert, + waitUntilNextExecution, + createTransactionMetric, + cleanupTargetIndices, + deleteAlert, +} from '../../lib'; +import { AlertDef, AlertParams } from '../../common/types'; +import { Alert } from '../../../../plugins/alerting/common'; +import { APM_METRIC_INDEX_NAME } from '../../common/constants'; + +// eslint-disable-next-line import/no-default-export +export default function registryRulesApiTest({ getService }: FtrProviderContext) { + const es = getService('es'); + const security = getService('security'); + + describe('Rule Registry API', () => { + before(async () => { + await createUser(security, User.apmWriteUser); + }); + + describe('with write permissions', () => { + it('does not bootstrap indices on plugin startup', async () => { + const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmWriteUser); + try { + const res = await es.indices.get({ + index: targetIndices[0], + expand_wildcards: 'open', + allow_no_indices: true, + }); + expect(res).to.be.empty(); + } catch (exc) { + expect(exc.statusCode).to.eql(404); + } + }); + + describe('when creating a rule', () => { + let createResponse: { + alert: Alert; + status: number; + }; + before(async () => { + await createApmMetricIndex(getService); + const alertDef: AlertDef = { + params: { + threshold: 30, + windowSize: 5, + windowUnit: 'm', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + serviceName: 'opbeans-go', + }, + consumer: 'apm', + alertTypeId: 'apm.transaction_error_rate', + schedule: { interval: '5s' }, + actions: [], + tags: ['apm', 'service.name:opbeans-go'], + notifyWhen: 'onActionGroupChange', + name: 'Failed transaction rate threshold | opbeans-go', + }; + createResponse = await createAlert(getService, User.apmWriteUser, alertDef); + }); + after(async () => { + await deleteAlert(getService, User.apmWriteUser, createResponse.alert.id); + await cleanupTargetIndices(getService, User.apmWriteUser); + }); + + it('writes alerts data to the alert indices', async () => { + expect(createResponse.status).to.be.below(299); + + expect(createResponse.alert).not.to.be(undefined); + let alert = await waitUntilNextExecution( + getService, + User.apmWriteUser, + createResponse.alert + ); + + const { body: targetIndices } = await getAlertsTargetIndices( + getService, + User.apmWriteUser + ); + + try { + const res = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + }, + }); + expect(res).to.be.empty(); + } catch (exc) { + expect(exc.message).contain('index_not_found_exception'); + } + + await es.index({ + index: APM_METRIC_INDEX_NAME, + body: createTransactionMetric({ + event: { + outcome: 'success', + }, + }), + refresh: true, + }); + + alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + + try { + const res = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + }, + }); + expect(res).to.be.empty(); + } catch (exc) { + expect(exc.message).contain('index_not_found_exception'); + } + + await es.index({ + index: APM_METRIC_INDEX_NAME, + body: createTransactionMetric({ + event: { + outcome: 'failure', + }, + }), + refresh: true, + }); + + alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + + const afterViolatingDataResponse = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], + }, + }); + + expect(afterViolatingDataResponse.hits.hits.length).to.be(1); + + const alertEvent = afterViolatingDataResponse.hits.hits[0].fields as Record; + + const exclude = ['@timestamp', ALERT_START, ALERT_UUID, ALERT_RULE_UUID, VERSION]; + + const toCompare = omit(alertEvent, exclude); + + expectSnapshot(toCompare).toMatch(); + + await es.bulk({ + index: APM_METRIC_INDEX_NAME, + body: [ + { index: {} }, + createTransactionMetric({ + event: { + outcome: 'success', + }, + }), + { index: {} }, + createTransactionMetric({ + event: { + outcome: 'success', + }, + }), + ], + refresh: true, + }); + + alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + + const afterRecoveryResponse = await es.search({ + index: targetIndices[0], + body: { + query: { + term: { + [EVENT_KIND]: 'signal', + }, + }, + size: 1, + sort: { + '@timestamp': 'desc', + }, + _source: false, + fields: [{ field: '*', include_unmapped: true }], + }, + }); + + expect(afterRecoveryResponse.hits.hits.length).to.be(1); + + const recoveredAlertEvent = afterRecoveryResponse.hits.hits[0].fields as Record< + string, + any + >; + + expect(recoveredAlertEvent[ALERT_STATUS]?.[0]).to.eql('recovered'); + expect(recoveredAlertEvent[ALERT_DURATION]?.[0]).to.be.greaterThan(0); + expect(new Date(recoveredAlertEvent[ALERT_END]?.[0]).getTime()).to.be.greaterThan(0); + + expectSnapshot( + omit(recoveredAlertEvent, exclude.concat([ALERT_DURATION, ALERT_END])) + ).toMatch(); + }); + }); + }); + + describe('with read permissions', () => { + it('does not bootstrap the apm rule indices', async () => { + const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmReadUser); + const errorOrUndefined = await es.indices + .get({ + index: targetIndices[0], + expand_wildcards: 'open', + allow_no_indices: false, + }) + .then(() => {}) + .catch((error) => { + return error.toString(); + }); + + expect(errorOrUndefined).not.to.be(undefined); + + expect(errorOrUndefined).to.contain('index_not_found_exception'); + }); + }); + }); +} From e8266188116a65eac197d7bb06bd185ce110350b Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Mon, 8 Nov 2021 15:22:24 -0700 Subject: [PATCH 4/8] removing old observability directory under api_integrations since it's not being used --- .../apis/observability/index.ts | 12 - .../apis/observability/registry_rules.ts | 273 ------------------ 2 files changed, 285 deletions(-) delete mode 100644 x-pack/test/api_integration/apis/observability/index.ts delete mode 100644 x-pack/test/api_integration/apis/observability/registry_rules.ts diff --git a/x-pack/test/api_integration/apis/observability/index.ts b/x-pack/test/api_integration/apis/observability/index.ts deleted file mode 100644 index 354a53f2999906..00000000000000 --- a/x-pack/test/api_integration/apis/observability/index.ts +++ /dev/null @@ -1,12 +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 { FtrProviderContext } from '../../ftr_provider_context'; - -export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('Observability', () => {}); -} diff --git a/x-pack/test/api_integration/apis/observability/registry_rules.ts b/x-pack/test/api_integration/apis/observability/registry_rules.ts deleted file mode 100644 index 22ac50105628ac..00000000000000 --- a/x-pack/test/api_integration/apis/observability/registry_rules.ts +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { - ALERT_DURATION, - ALERT_END, - ALERT_RULE_UUID, - ALERT_START, - ALERT_STATUS, - ALERT_UUID, - EVENT_KIND, - VERSION, -} from '@kbn/rule-data-utils'; -import { omit } from 'lodash'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { createUser, User } from './common/users'; -import { - getAlertsTargetIndices, - createApmMetricIndex, - createAlert, - waitUntilNextExecution, - createTransactionMetric, - cleanupTargetIndices, -} from '../../../observability_api_integration/lib'; -import { AlertDef, AlertParams } from './common/types'; -import { Alert } from '../../../../plugins/alerting/common'; -import { deleteAlert } from '../../../observability_api_integration/lib/delete_alert'; -import { APM_METRIC_INDEX_NAME } from './common/constants'; - -export default function registryRulesApiTest({ getService }: FtrProviderContext) { - const es = getService('es'); - const security = getService('security'); - - describe('Rule Registry API', () => { - before(async () => { - await createUser(security, User.apmReadUser); - await createUser(security, User.apmWriteUser); - }); - - describe('with write permissions', () => { - it('does not bootstrap indices on plugin startup', async () => { - const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmWriteUser); - try { - const res = await es.indices.get({ - index: targetIndices[0], - expand_wildcards: 'open', - allow_no_indices: true, - }); - expect(res).to.be.empty(); - } catch (exc) { - expect(exc.statusCode).to.eql(404); - } - }); - - describe('when creating a rule', () => { - let createResponse: { - alert: Alert; - status: number; - }; - before(async () => { - await createApmMetricIndex(getService); - const alertDef: AlertDef = { - params: { - threshold: 30, - windowSize: 5, - windowUnit: 'm', - transactionType: 'request', - environment: 'ENVIRONMENT_ALL', - serviceName: 'opbeans-go', - }, - consumer: 'apm', - alertTypeId: 'apm.transaction_error_rate', - schedule: { interval: '5s' }, - actions: [], - tags: ['apm', 'service.name:opbeans-go'], - notifyWhen: 'onActionGroupChange', - name: 'Failed transaction rate threshold | opbeans-go', - }; - createResponse = await createAlert(getService, User.apmWriteUser, alertDef); - }); - after(async () => { - await deleteAlert(getService, User.apmWriteUser, createResponse.alert.id); - await cleanupTargetIndices(getService, User.apmWriteUser); - }); - - it('writes alerts data to the alert indices', async () => { - expect(createResponse.status).to.be.below(299); - - expect(createResponse.alert).not.to.be(undefined); - let alert = await waitUntilNextExecution( - getService, - User.apmWriteUser, - createResponse.alert - ); - - const { body: targetIndices } = await getAlertsTargetIndices( - getService, - User.apmWriteUser - ); - - try { - const res = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - }, - }); - expect(res).to.be.empty(); - } catch (exc) { - expect(exc.message).contain('index_not_found_exception'); - } - - await es.index({ - index: APM_METRIC_INDEX_NAME, - body: createTransactionMetric({ - event: { - outcome: 'success', - }, - }), - refresh: true, - }); - - alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); - - try { - const res = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - }, - }); - expect(res).to.be.empty(); - } catch (exc) { - expect(exc.message).contain('index_not_found_exception'); - } - - await es.index({ - index: APM_METRIC_INDEX_NAME, - body: createTransactionMetric({ - event: { - outcome: 'failure', - }, - }), - refresh: true, - }); - - alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); - - const afterViolatingDataResponse = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - _source: false, - fields: [{ field: '*', include_unmapped: true }], - }, - }); - - expect(afterViolatingDataResponse.hits.hits.length).to.be(1); - - const alertEvent = afterViolatingDataResponse.hits.hits[0].fields as Record; - - const exclude = ['@timestamp', ALERT_START, ALERT_UUID, ALERT_RULE_UUID, VERSION]; - - const toCompare = omit(alertEvent, exclude); - - expectSnapshot(toCompare).toMatch(); - - await es.bulk({ - index: APM_METRIC_INDEX_NAME, - body: [ - { index: {} }, - createTransactionMetric({ - event: { - outcome: 'success', - }, - }), - { index: {} }, - createTransactionMetric({ - event: { - outcome: 'success', - }, - }), - ], - refresh: true, - }); - - alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); - - const afterRecoveryResponse = await es.search({ - index: targetIndices[0], - body: { - query: { - term: { - [EVENT_KIND]: 'signal', - }, - }, - size: 1, - sort: { - '@timestamp': 'desc', - }, - _source: false, - fields: [{ field: '*', include_unmapped: true }], - }, - }); - - expect(afterRecoveryResponse.hits.hits.length).to.be(1); - - const recoveredAlertEvent = afterRecoveryResponse.hits.hits[0].fields as Record< - string, - any - >; - - expect(recoveredAlertEvent[ALERT_STATUS]?.[0]).to.eql('recovered'); - expect(recoveredAlertEvent[ALERT_DURATION]?.[0]).to.be.greaterThan(0); - expect(new Date(recoveredAlertEvent[ALERT_END]?.[0]).getTime()).to.be.greaterThan(0); - - expectSnapshot( - omit(recoveredAlertEvent, exclude.concat([ALERT_DURATION, ALERT_END])) - ).toMatch(); - }); - }); - }); - - describe('with read permissions', () => { - it('does not bootstrap the apm rule indices', async () => { - const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmReadUser); - const errorOrUndefined = await es.indices - .get({ - index: targetIndices[0], - expand_wildcards: 'open', - allow_no_indices: false, - }) - .then(() => {}) - .catch((error) => { - return error.toString(); - }); - - expect(errorOrUndefined).not.to.be(undefined); - - expect(errorOrUndefined).to.contain('index_not_found_exception'); - }); - }); - }); -} From 3f68b2c350fd0a5f9d6fa12cdc4252e63008b4b5 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Tue, 9 Nov 2021 14:56:58 -0700 Subject: [PATCH 5/8] removing observability from api_integration tests --- x-pack/test/api_integration/apis/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index 2aed3be81c86ec..c3d08ba3066927 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -34,6 +34,5 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./searchprofiler')); loadTestFile(require.resolve('./painless_lab')); loadTestFile(require.resolve('./file_upload')); - loadTestFile(require.resolve('./observability')); }); } From 201574d706d71ea63628437d5a6072a6b2788536 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 10 Nov 2021 09:49:00 -0700 Subject: [PATCH 6/8] renaming file --- x-pack/test/observability_api_integration/basic/tests/index.ts | 2 +- .../basic/tests/{registry_rules.ts => rule_registry.ts} | 0 x-pack/test/observability_api_integration/trial/tests/index.ts | 2 +- .../trial/tests/{registry_rules.ts => rule_registry.ts} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename x-pack/test/observability_api_integration/basic/tests/{registry_rules.ts => rule_registry.ts} (100%) rename x-pack/test/observability_api_integration/trial/tests/{registry_rules.ts => rule_registry.ts} (100%) diff --git a/x-pack/test/observability_api_integration/basic/tests/index.ts b/x-pack/test/observability_api_integration/basic/tests/index.ts index 10a5e83378b7d8..538a7bbf9fc02f 100644 --- a/x-pack/test/observability_api_integration/basic/tests/index.ts +++ b/x-pack/test/observability_api_integration/basic/tests/index.ts @@ -12,6 +12,6 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr describe('Observability specs (basic)', function () { this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); - loadTestFile(require.resolve('./registry_rules')); + loadTestFile(require.resolve('./rule_registry')); }); } diff --git a/x-pack/test/observability_api_integration/basic/tests/registry_rules.ts b/x-pack/test/observability_api_integration/basic/tests/rule_registry.ts similarity index 100% rename from x-pack/test/observability_api_integration/basic/tests/registry_rules.ts rename to x-pack/test/observability_api_integration/basic/tests/rule_registry.ts diff --git a/x-pack/test/observability_api_integration/trial/tests/index.ts b/x-pack/test/observability_api_integration/trial/tests/index.ts index f58b14104d8892..f05350b74facca 100644 --- a/x-pack/test/observability_api_integration/trial/tests/index.ts +++ b/x-pack/test/observability_api_integration/trial/tests/index.ts @@ -12,6 +12,6 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont describe('Observability specs (trial)', function () { this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); - loadTestFile(require.resolve('./registry_rules')); + loadTestFile(require.resolve('./rule_registry')); }); } diff --git a/x-pack/test/observability_api_integration/trial/tests/registry_rules.ts b/x-pack/test/observability_api_integration/trial/tests/rule_registry.ts similarity index 100% rename from x-pack/test/observability_api_integration/trial/tests/registry_rules.ts rename to x-pack/test/observability_api_integration/trial/tests/rule_registry.ts From 18a2261f2a5267aa0d24a9100a31686e5c408457 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 10 Nov 2021 12:15:28 -0700 Subject: [PATCH 7/8] moving test to x-pack/test/rule_registry --- x-pack/scripts/functional_tests.js | 1 + .../basic/tests/index.ts | 1 - .../common/users.ts | 62 ------------------- .../lib/create_alert.ts | 19 ------ .../lib/get_alerts_target_indices.ts | 18 ------ .../trial/tests/index.ts | 1 - .../common/constants.ts | 0 .../lib/helpers}/cleanup_target_indices.ts | 8 +-- .../common/lib/helpers/create_alert.ts | 25 ++++++++ .../lib/helpers}/create_apm_metric_index.ts | 6 +- .../lib/helpers}/create_transaction_metric.ts | 2 +- .../common/lib/helpers}/delete_alert.ts | 22 ++++--- .../lib/helpers/get_alerts_target_indices.ts | 23 +++++++ .../common/lib/helpers}/index.ts | 0 .../lib/helpers}/wait_until_next_execution.ts | 23 ++++--- .../common/types.ts | 2 +- .../rule_registry/spaces_only/config_basic.ts | 16 +++++ .../spaces_only/tests/basic/bootstrap.ts} | 17 +++-- .../spaces_only/tests/basic/index.ts | 28 +++++++++ .../trial/__snapshots__/create_rule.snap} | 8 +-- .../spaces_only/tests/trial/create_rule.ts} | 61 ++++++------------ .../spaces_only/tests/trial/index.ts | 3 +- 22 files changed, 163 insertions(+), 183 deletions(-) delete mode 100644 x-pack/test/observability_api_integration/common/users.ts delete mode 100644 x-pack/test/observability_api_integration/lib/create_alert.ts delete mode 100644 x-pack/test/observability_api_integration/lib/get_alerts_target_indices.ts rename x-pack/test/{observability_api_integration => rule_registry}/common/constants.ts (100%) rename x-pack/test/{observability_api_integration/lib => rule_registry/common/lib/helpers}/cleanup_target_indices.ts (84%) create mode 100644 x-pack/test/rule_registry/common/lib/helpers/create_alert.ts rename x-pack/test/{observability_api_integration/lib => rule_registry/common/lib/helpers}/create_apm_metric_index.ts (92%) rename x-pack/test/{observability_api_integration/lib => rule_registry/common/lib/helpers}/create_transaction_metric.ts (94%) rename x-pack/test/{observability_api_integration/lib => rule_registry/common/lib/helpers}/delete_alert.ts (62%) create mode 100644 x-pack/test/rule_registry/common/lib/helpers/get_alerts_target_indices.ts rename x-pack/test/{observability_api_integration/lib => rule_registry/common/lib/helpers}/index.ts (100%) rename x-pack/test/{observability_api_integration/lib => rule_registry/common/lib/helpers}/wait_until_next_execution.ts (79%) rename x-pack/test/{observability_api_integration => rule_registry}/common/types.ts (92%) create mode 100644 x-pack/test/rule_registry/spaces_only/config_basic.ts rename x-pack/test/{observability_api_integration/basic/tests/rule_registry.ts => rule_registry/spaces_only/tests/basic/bootstrap.ts} (76%) create mode 100644 x-pack/test/rule_registry/spaces_only/tests/basic/index.ts rename x-pack/test/{observability_api_integration/trial/tests/__snapshots__/registry_rules.snap => rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap} (88%) rename x-pack/test/{observability_api_integration/trial/tests/rule_registry.ts => rule_registry/spaces_only/tests/trial/create_rule.ts} (79%) diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 77212274b6ade5..37938ce21beb19 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -45,6 +45,7 @@ const onlyNotInCoverageTests = [ require.resolve('../test/plugin_api_integration/config.ts'), require.resolve('../test/rule_registry/security_and_spaces/config_basic.ts'), require.resolve('../test/rule_registry/security_and_spaces/config_trial.ts'), + require.resolve('../test/rule_registry/spaces_only/config_basic.ts'), require.resolve('../test/rule_registry/spaces_only/config_trial.ts'), require.resolve('../test/security_api_integration/saml.config.ts'), require.resolve('../test/security_api_integration/session_idle.config.ts'), diff --git a/x-pack/test/observability_api_integration/basic/tests/index.ts b/x-pack/test/observability_api_integration/basic/tests/index.ts index 538a7bbf9fc02f..c62cf4be0d7c7a 100644 --- a/x-pack/test/observability_api_integration/basic/tests/index.ts +++ b/x-pack/test/observability_api_integration/basic/tests/index.ts @@ -12,6 +12,5 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr describe('Observability specs (basic)', function () { this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); - loadTestFile(require.resolve('./rule_registry')); }); } diff --git a/x-pack/test/observability_api_integration/common/users.ts b/x-pack/test/observability_api_integration/common/users.ts deleted file mode 100644 index e2712aa9137dc2..00000000000000 --- a/x-pack/test/observability_api_integration/common/users.ts +++ /dev/null @@ -1,62 +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 { SecurityService } from 'test/common/services/security/security'; - -export enum User { - apmReadUser = 'apm_read_user', - apmWriteUser = 'apm_write_user', -} - -export const roles = { - [User.apmReadUser]: { - kibana: [ - { - base: [], - feature: { ml: ['read'] }, - spaces: ['*'], - }, - ], - }, - [User.apmWriteUser]: { - kibana: [ - { - base: [], - feature: { ml: ['all'] }, - spaces: ['*'], - }, - ], - }, -}; - -export const users = { - [User.apmReadUser]: { - roles: ['viewer', User.apmReadUser], - }, - [User.apmWriteUser]: { - roles: ['editor', User.apmWriteUser], - }, -}; - -export const TEST_PASSWORD = 'changeme'; - -export async function createUser(security: SecurityService, user: User) { - const roleDef = roles[user]; - const userDef = users[user]; - - if (!roleDef || !userDef) { - throw new Error(`No configuration found for ${user}`); - } - - await security.role.create(user, roleDef); - - await security.user.create(user, { - full_name: user, - password: TEST_PASSWORD, - roles: userDef.roles, - }); -} diff --git a/x-pack/test/observability_api_integration/lib/create_alert.ts b/x-pack/test/observability_api_integration/lib/create_alert.ts deleted file mode 100644 index 6d896e6f962cee..00000000000000 --- a/x-pack/test/observability_api_integration/lib/create_alert.ts +++ /dev/null @@ -1,19 +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 { User, TEST_PASSWORD } from '../common/users'; -import { GetService, AlertDef } from '../common/types'; - -export const createAlert = async (getService: GetService, user: User, alertDef: AlertDef) => { - const supertest = getService('supertest'); - const { body: response, status } = await supertest - .post('/api/alerts/alert') - .auth(user, TEST_PASSWORD) - .send(alertDef) - .set('kbn-xsrf', 'foo'); - return { alert: response, status }; -}; diff --git a/x-pack/test/observability_api_integration/lib/get_alerts_target_indices.ts b/x-pack/test/observability_api_integration/lib/get_alerts_target_indices.ts deleted file mode 100644 index e73477ceb25dc4..00000000000000 --- a/x-pack/test/observability_api_integration/lib/get_alerts_target_indices.ts +++ /dev/null @@ -1,18 +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 { GetService } from '../common/types'; -import { User, TEST_PASSWORD } from '../common/users'; -import { ALERTS_TARGET_INDICES_URL } from '../common/constants'; - -export const getAlertsTargetIndices = async (getService: GetService, user: User) => { - const supertest = getService('supertest'); - return supertest - .get(ALERTS_TARGET_INDICES_URL) - .auth(user, TEST_PASSWORD) - .send() - .set('kbn-xsrf', 'foo'); -}; diff --git a/x-pack/test/observability_api_integration/trial/tests/index.ts b/x-pack/test/observability_api_integration/trial/tests/index.ts index f05350b74facca..037cf48275de28 100644 --- a/x-pack/test/observability_api_integration/trial/tests/index.ts +++ b/x-pack/test/observability_api_integration/trial/tests/index.ts @@ -12,6 +12,5 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont describe('Observability specs (trial)', function () { this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); - loadTestFile(require.resolve('./rule_registry')); }); } diff --git a/x-pack/test/observability_api_integration/common/constants.ts b/x-pack/test/rule_registry/common/constants.ts similarity index 100% rename from x-pack/test/observability_api_integration/common/constants.ts rename to x-pack/test/rule_registry/common/constants.ts diff --git a/x-pack/test/observability_api_integration/lib/cleanup_target_indices.ts b/x-pack/test/rule_registry/common/lib/helpers/cleanup_target_indices.ts similarity index 84% rename from x-pack/test/observability_api_integration/lib/cleanup_target_indices.ts rename to x-pack/test/rule_registry/common/lib/helpers/cleanup_target_indices.ts index 799e984941ed94..b0acaaa59014bd 100644 --- a/x-pack/test/observability_api_integration/lib/cleanup_target_indices.ts +++ b/x-pack/test/rule_registry/common/lib/helpers/cleanup_target_indices.ts @@ -6,13 +6,13 @@ */ import expect from '@kbn/expect'; -import { GetService } from '../common/types'; -import { User } from '../common/users'; +import { GetService } from '../../types'; +import { User } from '../authentication/types'; import { getAlertsTargetIndices } from './get_alerts_target_indices'; -export const cleanupTargetIndices = async (getService: GetService, user: User) => { +export const cleanupTargetIndices = async (getService: GetService, user: User, spaceId: string) => { const es = getService('es'); - const { body: targetIndices } = await getAlertsTargetIndices(getService, user); + const { body: targetIndices } = await getAlertsTargetIndices(getService, user, spaceId); try { const aliasMap = await es.indices.getAlias({ name: targetIndices, diff --git a/x-pack/test/rule_registry/common/lib/helpers/create_alert.ts b/x-pack/test/rule_registry/common/lib/helpers/create_alert.ts new file mode 100644 index 00000000000000..40d43ac80d210a --- /dev/null +++ b/x-pack/test/rule_registry/common/lib/helpers/create_alert.ts @@ -0,0 +1,25 @@ +/* + * 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 { User } from '../authentication/types'; +import { GetService, AlertDef } from '../../types'; +import { getSpaceUrlPrefix } from '../authentication/spaces'; + +export const createAlert = async ( + getService: GetService, + user: User, + spaceId: string, + alertDef: AlertDef +) => { + const supertest = getService('supertestWithoutAuth'); + const { body: response, status } = await supertest + .post(`${getSpaceUrlPrefix(spaceId)}/api/alerts/alert`) + .auth(user.username, user.password) + .send(alertDef) + .set('kbn-xsrf', 'foo'); + return { alert: response, status }; +}; diff --git a/x-pack/test/observability_api_integration/lib/create_apm_metric_index.ts b/x-pack/test/rule_registry/common/lib/helpers/create_apm_metric_index.ts similarity index 92% rename from x-pack/test/observability_api_integration/lib/create_apm_metric_index.ts rename to x-pack/test/rule_registry/common/lib/helpers/create_apm_metric_index.ts index 607cdf3e78f8c5..fab0f79d5a15f9 100644 --- a/x-pack/test/observability_api_integration/lib/create_apm_metric_index.ts +++ b/x-pack/test/rule_registry/common/lib/helpers/create_apm_metric_index.ts @@ -4,12 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { APM_METRIC_INDEX_NAME } from '../common/constants'; -import { GetService } from '../common/types'; +import { APM_METRIC_INDEX_NAME } from '../../constants'; +import { GetService } from '../../types'; export const createApmMetricIndex = async (getService: GetService) => { const es = getService('es'); - return es.indices.create({ + await es.indices.create({ index: APM_METRIC_INDEX_NAME, body: { mappings: { diff --git a/x-pack/test/observability_api_integration/lib/create_transaction_metric.ts b/x-pack/test/rule_registry/common/lib/helpers/create_transaction_metric.ts similarity index 94% rename from x-pack/test/observability_api_integration/lib/create_transaction_metric.ts rename to x-pack/test/rule_registry/common/lib/helpers/create_transaction_metric.ts index 1af7c442fd0014..0675f919fbc4e2 100644 --- a/x-pack/test/observability_api_integration/lib/create_transaction_metric.ts +++ b/x-pack/test/rule_registry/common/lib/helpers/create_transaction_metric.ts @@ -6,7 +6,7 @@ */ import { merge } from 'lodash'; -import { INDEXING_DELAY } from '../common/constants'; +import { INDEXING_DELAY } from '../../constants'; export const createTransactionMetric = (override: Record) => { const now = Date.now(); diff --git a/x-pack/test/observability_api_integration/lib/delete_alert.ts b/x-pack/test/rule_registry/common/lib/helpers/delete_alert.ts similarity index 62% rename from x-pack/test/observability_api_integration/lib/delete_alert.ts rename to x-pack/test/rule_registry/common/lib/helpers/delete_alert.ts index 8d442fcc950451..209b182a958c5d 100644 --- a/x-pack/test/observability_api_integration/lib/delete_alert.ts +++ b/x-pack/test/rule_registry/common/lib/helpers/delete_alert.ts @@ -5,19 +5,25 @@ * 2.0. */ -import { APM_METRIC_INDEX_NAME } from '../common/constants'; -import { GetService } from '../common/types'; -import { User, TEST_PASSWORD } from '../common/users'; +import { APM_METRIC_INDEX_NAME } from '../../constants'; +import { GetService } from '../../types'; +import { getSpaceUrlPrefix } from '../authentication/spaces'; +import { User } from '../authentication/types'; import { getAlertsTargetIndices } from './get_alerts_target_indices'; -export const deleteAlert = async (getService: GetService, user: User, id: string | undefined) => { +export const deleteAlert = async ( + getService: GetService, + user: User, + spaceId: string, + id: string | undefined +) => { const es = getService('es'); - const supertest = getService('supertest'); - const { body: targetIndices } = await getAlertsTargetIndices(getService, user); + const supertest = getService('supertestWithoutAuth'); + const { body: targetIndices } = await getAlertsTargetIndices(getService, user, spaceId); if (id) { const { body, status } = await supertest - .delete(`/api/alerts/alert/${id}`) - .auth(user, TEST_PASSWORD) + .delete(`${getSpaceUrlPrefix(spaceId)}/api/alerts/alert/${id}`) + .auth(user.username, user.password) .set('kbn-xsrf', 'foo'); if (status >= 300) { diff --git a/x-pack/test/rule_registry/common/lib/helpers/get_alerts_target_indices.ts b/x-pack/test/rule_registry/common/lib/helpers/get_alerts_target_indices.ts new file mode 100644 index 00000000000000..c78f97f30de746 --- /dev/null +++ b/x-pack/test/rule_registry/common/lib/helpers/get_alerts_target_indices.ts @@ -0,0 +1,23 @@ +/* + * 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 { ALERTS_TARGET_INDICES_URL } from '../../constants'; +import { GetService } from '../../types'; +import { User } from '../authentication/types'; +import { getSpaceUrlPrefix } from '../authentication/spaces'; + +export const getAlertsTargetIndices = async ( + getService: GetService, + user: User, + spaceId: string +) => { + const supertest = getService('supertestWithoutAuth'); + return supertest + .get(`${getSpaceUrlPrefix(spaceId)}${ALERTS_TARGET_INDICES_URL}`) + .auth(user.username, user.password) + .send() + .set('kbn-xsrf', 'foo'); +}; diff --git a/x-pack/test/observability_api_integration/lib/index.ts b/x-pack/test/rule_registry/common/lib/helpers/index.ts similarity index 100% rename from x-pack/test/observability_api_integration/lib/index.ts rename to x-pack/test/rule_registry/common/lib/helpers/index.ts diff --git a/x-pack/test/observability_api_integration/lib/wait_until_next_execution.ts b/x-pack/test/rule_registry/common/lib/helpers/wait_until_next_execution.ts similarity index 79% rename from x-pack/test/observability_api_integration/lib/wait_until_next_execution.ts rename to x-pack/test/rule_registry/common/lib/helpers/wait_until_next_execution.ts index 4dd9380aa4a12f..8bc325c4a6bb76 100644 --- a/x-pack/test/observability_api_integration/lib/wait_until_next_execution.ts +++ b/x-pack/test/rule_registry/common/lib/helpers/wait_until_next_execution.ts @@ -4,20 +4,22 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { User, TEST_PASSWORD } from '../common/users'; -import { GetService } from '../common/types'; +import { GetService } from '../../types'; import { getAlertsTargetIndices } from './get_alerts_target_indices'; -import { BULK_INDEX_DELAY, MAX_POLLS } from '../common/constants'; -import { Alert } from '../../../plugins/alerting/common'; +import { BULK_INDEX_DELAY, MAX_POLLS } from '../../constants'; +import { Alert } from '../../../../../plugins/alerting/common'; +import { getSpaceUrlPrefix } from '../authentication/spaces'; +import { User } from '../authentication/types'; export async function waitUntilNextExecution( getService: GetService, user: User, alert: Alert, + spaceId: string, intervalInSeconds: number = 1, count: number = 0 ): Promise { - const supertest = getService('supertest'); + const supertest = getService('supertestWithoutAuth'); const es = getService('es'); await new Promise((resolve) => { @@ -25,16 +27,17 @@ export async function waitUntilNextExecution( }); const { body, status } = await supertest - .get(`/api/alerts/alert/${alert.id}`) - .auth(user, TEST_PASSWORD) + .get(`${getSpaceUrlPrefix(spaceId)}/api/alerts/alert/${alert.id}`) + .auth(user.username, user.password) .set('kbn-xsrf', 'foo'); const { body: targetIndices, status: targetIndicesStatus } = await getAlertsTargetIndices( getService, - user + user, + spaceId ); if (targetIndices.length === 0) { - const error = new Error('Error getting alert'); + const error = new Error('Error getting target indices'); Object.assign(error, { response: { body: targetIndices, status: targetIndicesStatus } }); throw error; } @@ -74,5 +77,5 @@ export async function waitUntilNextExecution( throw new Error('Maximum number of polls exceeded'); } - return waitUntilNextExecution(getService, user, alert, intervalInSeconds, count + 1); + return waitUntilNextExecution(getService, user, alert, spaceId, intervalInSeconds, count + 1); } diff --git a/x-pack/test/observability_api_integration/common/types.ts b/x-pack/test/rule_registry/common/types.ts similarity index 92% rename from x-pack/test/observability_api_integration/common/types.ts rename to x-pack/test/rule_registry/common/types.ts index 8b9a569f4ed26e..63d45b0d850d9e 100644 --- a/x-pack/test/observability_api_integration/common/types.ts +++ b/x-pack/test/rule_registry/common/types.ts @@ -6,7 +6,7 @@ */ import { GenericFtrProviderContext } from '@kbn/test'; import { Alert, AlertTypeParams } from '../../../plugins/alerting/common'; -import { services } from '../../api_integration/services'; +import { services } from './services'; export type GetService = GenericFtrProviderContext['getService']; diff --git a/x-pack/test/rule_registry/spaces_only/config_basic.ts b/x-pack/test/rule_registry/spaces_only/config_basic.ts new file mode 100644 index 00000000000000..5a2ee4c1c11783 --- /dev/null +++ b/x-pack/test/rule_registry/spaces_only/config_basic.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from '../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('spaces_only', { + license: 'basic', + disabledPlugins: ['security'], + ssl: false, + testFiles: [require.resolve('./tests/basic')], +}); diff --git a/x-pack/test/observability_api_integration/basic/tests/rule_registry.ts b/x-pack/test/rule_registry/spaces_only/tests/basic/bootstrap.ts similarity index 76% rename from x-pack/test/observability_api_integration/basic/tests/rule_registry.ts rename to x-pack/test/rule_registry/spaces_only/tests/basic/bootstrap.ts index 380163753c57d5..ccae5189f6d30c 100644 --- a/x-pack/test/observability_api_integration/basic/tests/rule_registry.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/basic/bootstrap.ts @@ -6,23 +6,22 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { createUser, User } from '../../common/users'; -import { getAlertsTargetIndices } from '../../lib'; +import type { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { obsOnlyRead } from '../../../common/lib/authentication/users'; +import { getAlertsTargetIndices } from '../../../common/lib/helpers'; // eslint-disable-next-line import/no-default-export export default function registryRulesApiTest({ getService }: FtrProviderContext) { const es = getService('es'); - const security = getService('security'); describe('Rule Registry API', () => { - before(async () => { - await createUser(security, User.apmReadUser); - }); - describe('with read permissions', () => { it('does not bootstrap the apm rule indices', async () => { - const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmReadUser); + const { body: targetIndices } = await getAlertsTargetIndices( + getService, + obsOnlyRead, + 'space1' + ); const errorOrUndefined = await es.indices .get({ index: targetIndices[0], diff --git a/x-pack/test/rule_registry/spaces_only/tests/basic/index.ts b/x-pack/test/rule_registry/spaces_only/tests/basic/index.ts new file mode 100644 index 00000000000000..aeb2b085ad3796 --- /dev/null +++ b/x-pack/test/rule_registry/spaces_only/tests/basic/index.ts @@ -0,0 +1,28 @@ +/* + * 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 { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { createSpaces, deleteSpaces } from '../../../common/lib/authentication'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile, getService }: FtrProviderContext): void => { + describe('rule registry spaces only: trial', function () { + // Fastest ciGroup for the moment. + this.tags('ciGroup5'); + + before(async () => { + await createSpaces(getService); + }); + + after(async () => { + await deleteSpaces(getService); + }); + + // Basic + loadTestFile(require.resolve('./bootstrap')); + }); +}; diff --git a/x-pack/test/observability_api_integration/trial/tests/__snapshots__/registry_rules.snap b/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap similarity index 88% rename from x-pack/test/observability_api_integration/trial/tests/__snapshots__/registry_rules.snap rename to x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap index b990fc3a744038..cf0b942faa5f84 100644 --- a/x-pack/test/observability_api_integration/trial/tests/__snapshots__/registry_rules.snap +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Observability specs (trial) Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 1`] = ` +exports[`rule registry spaces only: trial Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 1`] = ` Object { "event.action": Array [ "open", @@ -45,7 +45,7 @@ Object { "open", ], "kibana.space_ids": Array [ - "default", + "space1", ], "processor.event": Array [ "transaction", @@ -63,7 +63,7 @@ Object { } `; -exports[`Observability specs (trial) Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 2`] = ` +exports[`rule registry spaces only: trial Rule Registry API with write permissions when creating a rule writes alerts data to the alert indices 2`] = ` Object { "event.action": Array [ "close", @@ -105,7 +105,7 @@ Object { "open", ], "kibana.space_ids": Array [ - "default", + "space1", ], "processor.event": Array [ "transaction", diff --git a/x-pack/test/observability_api_integration/trial/tests/rule_registry.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/create_rule.ts similarity index 79% rename from x-pack/test/observability_api_integration/trial/tests/rule_registry.ts rename to x-pack/test/rule_registry/spaces_only/tests/trial/create_rule.ts index db2734f6f4df86..ac36bad1f595ba 100644 --- a/x-pack/test/observability_api_integration/trial/tests/rule_registry.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/create_rule.ts @@ -17,8 +17,7 @@ import { VERSION, } from '@kbn/rule-data-utils'; import { omit } from 'lodash'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { createUser, User } from '../../common/users'; +import type { FtrProviderContext } from '../../../common/ftr_provider_context'; import { getAlertsTargetIndices, createApmMetricIndex, @@ -27,24 +26,22 @@ import { createTransactionMetric, cleanupTargetIndices, deleteAlert, -} from '../../lib'; -import { AlertDef, AlertParams } from '../../common/types'; -import { Alert } from '../../../../plugins/alerting/common'; -import { APM_METRIC_INDEX_NAME } from '../../common/constants'; +} from '../../../common/lib/helpers'; +import { AlertDef, AlertParams } from '../../../common/types'; +import { Alert } from '../../../../../plugins/alerting/common'; +import { APM_METRIC_INDEX_NAME } from '../../../common/constants'; +import { obsOnly } from '../../../common/lib/authentication/users'; + +const SPACE_ID = 'space1'; // eslint-disable-next-line import/no-default-export export default function registryRulesApiTest({ getService }: FtrProviderContext) { const es = getService('es'); - const security = getService('security'); describe('Rule Registry API', () => { - before(async () => { - await createUser(security, User.apmWriteUser); - }); - describe('with write permissions', () => { it('does not bootstrap indices on plugin startup', async () => { - const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmWriteUser); + const { body: targetIndices } = await getAlertsTargetIndices(getService, obsOnly, SPACE_ID); try { const res = await es.indices.get({ index: targetIndices[0], @@ -81,11 +78,11 @@ export default function registryRulesApiTest({ getService }: FtrProviderContext) notifyWhen: 'onActionGroupChange', name: 'Failed transaction rate threshold | opbeans-go', }; - createResponse = await createAlert(getService, User.apmWriteUser, alertDef); + createResponse = await createAlert(getService, obsOnly, SPACE_ID, alertDef); }); after(async () => { - await deleteAlert(getService, User.apmWriteUser, createResponse.alert.id); - await cleanupTargetIndices(getService, User.apmWriteUser); + await deleteAlert(getService, obsOnly, SPACE_ID, createResponse.alert.id); + await cleanupTargetIndices(getService, obsOnly, SPACE_ID); }); it('writes alerts data to the alert indices', async () => { @@ -94,13 +91,15 @@ export default function registryRulesApiTest({ getService }: FtrProviderContext) expect(createResponse.alert).not.to.be(undefined); let alert = await waitUntilNextExecution( getService, - User.apmWriteUser, - createResponse.alert + obsOnly, + createResponse.alert, + SPACE_ID ); const { body: targetIndices } = await getAlertsTargetIndices( getService, - User.apmWriteUser + obsOnly, + SPACE_ID ); try { @@ -133,7 +132,7 @@ export default function registryRulesApiTest({ getService }: FtrProviderContext) refresh: true, }); - alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + alert = await waitUntilNextExecution(getService, obsOnly, alert, SPACE_ID); try { const res = await es.search({ @@ -165,7 +164,7 @@ export default function registryRulesApiTest({ getService }: FtrProviderContext) refresh: true, }); - alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + alert = await waitUntilNextExecution(getService, obsOnly, alert, SPACE_ID); const afterViolatingDataResponse = await es.search({ index: targetIndices[0], @@ -213,7 +212,7 @@ export default function registryRulesApiTest({ getService }: FtrProviderContext) refresh: true, }); - alert = await waitUntilNextExecution(getService, User.apmWriteUser, alert); + alert = await waitUntilNextExecution(getService, obsOnly, alert, SPACE_ID); const afterRecoveryResponse = await es.search({ index: targetIndices[0], @@ -249,25 +248,5 @@ export default function registryRulesApiTest({ getService }: FtrProviderContext) }); }); }); - - describe('with read permissions', () => { - it('does not bootstrap the apm rule indices', async () => { - const { body: targetIndices } = await getAlertsTargetIndices(getService, User.apmReadUser); - const errorOrUndefined = await es.indices - .get({ - index: targetIndices[0], - expand_wildcards: 'open', - allow_no_indices: false, - }) - .then(() => {}) - .catch((error) => { - return error.toString(); - }); - - expect(errorOrUndefined).not.to.be(undefined); - - expect(errorOrUndefined).to.contain('index_not_found_exception'); - }); - }); }); } diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts index 6deba4c68d0e26..c8fc677eb06704 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts @@ -22,8 +22,9 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { await deleteSpaces(getService); }); - // Basic + // Trial loadTestFile(require.resolve('./get_alert_by_id')); loadTestFile(require.resolve('./update_alert')); + loadTestFile(require.resolve('./create_rule')); }); }; From 778f014460501a892eec495086c30609a6fd279e Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 17 Nov 2021 12:40:15 -0700 Subject: [PATCH 8/8] Adding error handing to cleanupTargetIndices --- .../common/lib/helpers/cleanup_target_indices.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/test/rule_registry/common/lib/helpers/cleanup_target_indices.ts b/x-pack/test/rule_registry/common/lib/helpers/cleanup_target_indices.ts index b0acaaa59014bd..a249c57d8c3dcc 100644 --- a/x-pack/test/rule_registry/common/lib/helpers/cleanup_target_indices.ts +++ b/x-pack/test/rule_registry/common/lib/helpers/cleanup_target_indices.ts @@ -12,8 +12,8 @@ import { getAlertsTargetIndices } from './get_alerts_target_indices'; export const cleanupTargetIndices = async (getService: GetService, user: User, spaceId: string) => { const es = getService('es'); - const { body: targetIndices } = await getAlertsTargetIndices(getService, user, spaceId); try { + const { body: targetIndices } = await getAlertsTargetIndices(getService, user, spaceId); const aliasMap = await es.indices.getAlias({ name: targetIndices, allow_no_indices: true, @@ -21,8 +21,10 @@ export const cleanupTargetIndices = async (getService: GetService, user: User, s }); const indices = Object.keys(aliasMap); expect(indices.length > 0).to.be(true); - return es.indices.delete({ index: indices }); - } catch (err) { - // do nothing + return es.indices.delete({ index: indices }, { ignore: [404] }); + } catch (error) { + if (error.meta.statusCode !== 404) { + throw error; + } } };