Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Metrics UI] Remove UUID from Alert Instance IDs #71335

Merged
merged 6 commits into from
Jul 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions x-pack/plugins/alerts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,13 +482,15 @@ A schedule is structured such that the key specifies the format you wish to use
We currently support the _Interval format_ which specifies the interval in seconds, minutes, hours or days at which the alert should execute.
Example: `{ interval: "10s" }`, `{ interval: "5m" }`, `{ interval: "1h" }`, `{ interval: "1d" }`.

There are plans to support multiple other schedule formats in the near fuiture.
There are plans to support multiple other schedule formats in the near future.

## Alert instance factory

**alertInstanceFactory(id)**

One service passed in to alert types is an alert instance factory. This factory creates instances of alerts and must be used in order to execute actions. The id you give to the alert instance factory is a unique identifier to the alert instance (ex: server identifier if the instance is about the server). The instance factory will use this identifier to retrieve the state of previous instances with the same id. These instances support state persisting between alert type execution, but will clear out once the alert instance stops executing.
One service passed in to alert types is an alert instance factory. This factory creates instances of alerts and must be used in order to execute actions. The `id` you give to the alert instance factory is a unique identifier to the alert instance (ex: server identifier if the instance is about the server). The instance factory will use this identifier to retrieve the state of previous instances with the same `id`. These instances support state persisting between alert type execution, but will clear out once the alert instance stops executing.

Note that the `id` only needs to be unique **within the scope of a specific alert**, not unique across all alerts or alert types. For example, Alert 1 and Alert 2 can both create an alert instance with an `id` of `"a"` without conflicting with one another. But if Alert 1 creates 2 alert instances, then they must be differentiated with `id`s of `"a"` and `"b"`.

This factory returns an instance of `AlertInstance`. The alert instance class has the following methods, note that we have removed the methods that you shouldn't touch.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ interface InventoryMetricThresholdParams {
alertOnNoData?: boolean;
}

export const createInventoryMetricThresholdExecutor = (
libs: InfraBackendLibs,
alertId: string
) => async ({ services, params }: AlertExecutorOptions) => {
export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) => async ({
services,
params,
}: AlertExecutorOptions) => {
const {
criteria,
filterQuery,
Expand All @@ -54,7 +54,7 @@ export const createInventoryMetricThresholdExecutor = (

const inventoryItems = Object.keys(first(results) as any);
for (const item of inventoryItems) {
const alertInstance = services.alertInstanceFactory(`${item}::${alertId}`);
const alertInstance = services.alertInstanceFactory(`${item}`);
// AND logic; all criteria must be across the threshold
const shouldAlertFire = results.every((result) => result[item].shouldFire);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
*/
import { i18n } from '@kbn/i18n';
import { schema } from '@kbn/config-schema';
import { curry } from 'lodash';
import uuid from 'uuid';
import {
createInventoryMetricThresholdExecutor,
FIRED_ACTIONS,
Expand Down Expand Up @@ -43,7 +41,7 @@ export const registerMetricInventoryThresholdAlertType = (libs: InfraBackendLibs
defaultActionGroupId: FIRED_ACTIONS.id,
actionGroups: [FIRED_ACTIONS],
producer: 'metrics',
executor: curry(createInventoryMetricThresholdExecutor)(libs, uuid.v4()),
executor: createInventoryMetricThresholdExecutor(libs),
actionVariables: {
context: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ let persistAlertInstances = false; // eslint-disable-line

describe('The metric threshold alert type', () => {
describe('querying the entire infrastructure', () => {
const instanceID = '*::test';
const instanceID = '*';
const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') =>
executor({
services,
Expand Down Expand Up @@ -120,8 +120,8 @@ describe('The metric threshold alert type', () => {
],
},
});
const instanceIdA = 'a::test';
const instanceIdB = 'b::test';
const instanceIdA = 'a';
const instanceIdB = 'b';
test('sends an alert when all groups pass the threshold', async () => {
await execute(Comparator.GT, [0.75]);
expect(mostRecentAction(instanceIdA).id).toBe(FIRED_ACTIONS.id);
Expand Down Expand Up @@ -177,28 +177,28 @@ describe('The metric threshold alert type', () => {
},
});
test('sends an alert when all criteria cross the threshold', async () => {
const instanceID = '*::test';
const instanceID = '*';
await execute(Comparator.GT_OR_EQ, [1.0], [3.0]);
expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id);
expect(getState(instanceID).alertState).toBe(AlertStates.ALERT);
});
test('sends no alert when some, but not all, criteria cross the threshold', async () => {
const instanceID = '*::test';
const instanceID = '*';
await execute(Comparator.LT_OR_EQ, [1.0], [3.0]);
expect(mostRecentAction(instanceID)).toBe(undefined);
expect(getState(instanceID).alertState).toBe(AlertStates.OK);
});
test('alerts only on groups that meet all criteria when querying with a groupBy parameter', async () => {
const instanceIdA = 'a::test';
const instanceIdB = 'b::test';
const instanceIdA = 'a';
const instanceIdB = 'b';
await execute(Comparator.GT_OR_EQ, [1.0], [3.0], 'something');
expect(mostRecentAction(instanceIdA).id).toBe(FIRED_ACTIONS.id);
expect(getState(instanceIdA).alertState).toBe(AlertStates.ALERT);
expect(mostRecentAction(instanceIdB)).toBe(undefined);
expect(getState(instanceIdB).alertState).toBe(AlertStates.OK);
});
test('sends all criteria to the action context', async () => {
const instanceID = '*::test';
const instanceID = '*';
await execute(Comparator.GT_OR_EQ, [1.0], [3.0]);
const { action } = mostRecentAction(instanceID);
const reasons = action.reason.split('\n');
Expand All @@ -212,7 +212,7 @@ describe('The metric threshold alert type', () => {
});
});
describe('querying with the count aggregator', () => {
const instanceID = '*::test';
const instanceID = '*';
const execute = (comparator: Comparator, threshold: number[]) =>
executor({
services,
Expand All @@ -238,7 +238,7 @@ describe('The metric threshold alert type', () => {
});
});
describe('querying with the p99 aggregator', () => {
const instanceID = '*::test';
const instanceID = '*';
const execute = (comparator: Comparator, threshold: number[]) =>
executor({
services,
Expand All @@ -264,7 +264,7 @@ describe('The metric threshold alert type', () => {
});
});
describe('querying with the p95 aggregator', () => {
const instanceID = '*::test';
const instanceID = '*';
const execute = (comparator: Comparator, threshold: number[]) =>
executor({
services,
Expand All @@ -290,7 +290,7 @@ describe('The metric threshold alert type', () => {
});
});
describe("querying a metric that hasn't reported data", () => {
const instanceID = '*::test';
const instanceID = '*';
const execute = (alertOnNoData: boolean) =>
executor({
services,
Expand Down Expand Up @@ -319,9 +319,10 @@ describe('The metric threshold alert type', () => {
});

// describe('querying a metric that later recovers', () => {
// const instanceID = '*::test';
// const instanceID = '*';
// const execute = (threshold: number[]) =>
// executor({
//
// services,
// params: {
// criteria: [
Expand Down Expand Up @@ -379,7 +380,7 @@ const mockLibs: any = {
configuration: createMockStaticConfiguration({}),
};

const executor = createMetricThresholdExecutor(mockLibs, 'test') as (opts: {
const executor = createMetricThresholdExecutor(mockLibs) as (opts: {
params: AlertExecutorOptions['params'];
services: { callCluster: AlertExecutorOptions['params']['callCluster'] };
}) => Promise<void>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { AlertStates } from './types';
import { evaluateAlert } from './lib/evaluate_alert';

export const createMetricThresholdExecutor = (libs: InfraBackendLibs, alertId: string) =>
export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
async function (options: AlertExecutorOptions) {
const { services, params } = options;
const { criteria } = params;
Expand All @@ -36,7 +36,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs, alertId: s
// Because each alert result has the same group definitions, just grap the groups from the first one.
const groups = Object.keys(first(alertResults) as any);
for (const group of groups) {
const alertInstance = services.alertInstanceFactory(`${group}::${alertId}`);
const alertInstance = services.alertInstanceFactory(`${group}`);

// AND logic; all criteria must be across the threshold
const shouldAlertFire = alertResults.every((result) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import uuid from 'uuid';
import { schema } from '@kbn/config-schema';
import { curry } from 'lodash';
import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer';
import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor';
import { METRIC_THRESHOLD_ALERT_TYPE_ID, Comparator } from './types';
Expand Down Expand Up @@ -107,7 +105,7 @@ export function registerMetricThresholdAlertType(libs: InfraBackendLibs) {
},
defaultActionGroupId: FIRED_ACTIONS.id,
actionGroups: [FIRED_ACTIONS],
executor: curry(createMetricThresholdExecutor)(libs, uuid.v4()),
executor: createMetricThresholdExecutor(libs),
actionVariables: {
context: [
{ name: 'group', description: groupActionVariableDescription },
Expand Down