From a5d692dfee2017438d377b4f77acb49020fbff25 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 23 Nov 2021 20:36:01 +0100 Subject: [PATCH] [Uptime] Look for complete down check in the monitor status alert rule (#118999) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../requests/get_monitor_availability.test.ts | 25 +++++ .../lib/requests/get_monitor_availability.ts | 7 ++ .../lib/requests/get_monitor_status.test.ts | 80 ++++++++++++-- .../server/lib/requests/get_monitor_status.ts | 103 +++++++++++++----- 4 files changed, 177 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.test.ts index ee8386dfce5d..4133d2a4109b 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.test.ts @@ -197,6 +197,11 @@ describe('monitor availability', () => { "query": Object { "bool": Object { "filter": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, Object { "range": Object { "@timestamp": Object { @@ -374,6 +379,11 @@ describe('monitor availability', () => { "query": Object { "bool": Object { "filter": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, Object { "range": Object { "@timestamp": Object { @@ -688,6 +698,11 @@ describe('monitor availability', () => { "query": Object { "bool": Object { "filter": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, Object { "range": Object { "@timestamp": Object { @@ -786,6 +801,11 @@ describe('monitor availability', () => { "query": Object { "bool": Object { "filter": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, Object { "range": Object { "@timestamp": Object { @@ -905,6 +925,11 @@ describe('monitor availability', () => { "query": Object { "bool": Object { "filter": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, Object { "range": Object { "@timestamp": Object { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts index 4d51a4857eca..2a9bc86db60b 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_availability.ts @@ -9,6 +9,7 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { GetMonitorAvailabilityParams, Ping } from '../../../common/runtime_types'; import { asMutableArray } from '../../../common/utils/as_mutable_array'; import { AfterKey } from './get_monitor_status'; +import { UNNAMED_LOCATION } from '../../../common/constants'; export interface AvailabilityKey { monitorId: string; @@ -28,6 +29,7 @@ export const formatBuckets = async (buckets: any[]): Promise ({ ...key, + location: key.location === null ? UNNAMED_LOCATION : (key.location as string), monitorInfo: fields?.hits?.hits?.[0]?._source, up: up_sum.value, down: down_sum.value, @@ -60,6 +62,11 @@ export const getMonitorAvailability: UMElasticsearchQueryFn< query: { bool: { filter: [ + { + exists: { + field: 'summary', + }, + }, { range: { '@timestamp': { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts index 4a790ad2a1e9..fba1f57868e0 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.test.ts @@ -105,6 +105,11 @@ describe('getMonitorStatus', () => { "fields": Object { "top_hits": Object { "size": 1, + "sort": Array [ + Object { + "@timestamp": "desc", + }, + ], }, }, }, @@ -141,8 +146,15 @@ describe('getMonitorStatus', () => { "bool": Object { "filter": Array [ Object { - "term": Object { - "monitor.status": "down", + "exists": Object { + "field": "summary", + }, + }, + Object { + "range": Object { + "summary.down": Object { + "gt": "0", + }, }, }, Object { @@ -234,6 +246,11 @@ describe('getMonitorStatus', () => { "fields": Object { "top_hits": Object { "size": 1, + "sort": Array [ + Object { + "@timestamp": "desc", + }, + ], }, }, }, @@ -270,8 +287,15 @@ describe('getMonitorStatus', () => { "bool": Object { "filter": Array [ Object { - "term": Object { - "monitor.status": "down", + "exists": Object { + "field": "summary", + }, + }, + Object { + "range": Object { + "summary.down": Object { + "gt": "0", + }, }, }, Object { @@ -400,6 +424,11 @@ describe('getMonitorStatus', () => { "fields": Object { "top_hits": Object { "size": 1, + "sort": Array [ + Object { + "@timestamp": "desc", + }, + ], }, }, }, @@ -436,8 +465,15 @@ describe('getMonitorStatus', () => { "bool": Object { "filter": Array [ Object { - "term": Object { - "monitor.status": "down", + "exists": Object { + "field": "summary", + }, + }, + Object { + "range": Object { + "summary.down": Object { + "gt": "0", + }, }, }, Object { @@ -559,6 +595,11 @@ describe('getMonitorStatus', () => { "fields": Object { "top_hits": Object { "size": 1, + "sort": Array [ + Object { + "@timestamp": "desc", + }, + ], }, }, }, @@ -595,8 +636,15 @@ describe('getMonitorStatus', () => { "bool": Object { "filter": Array [ Object { - "term": Object { - "monitor.status": "down", + "exists": Object { + "field": "summary", + }, + }, + Object { + "range": Object { + "summary.down": Object { + "gt": "0", + }, }, }, Object { @@ -693,6 +741,11 @@ describe('getMonitorStatus', () => { "fields": Object { "top_hits": Object { "size": 1, + "sort": Array [ + Object { + "@timestamp": "desc", + }, + ], }, }, }, @@ -729,8 +782,15 @@ describe('getMonitorStatus', () => { "bool": Object { "filter": Array [ Object { - "term": Object { - "monitor.status": "down", + "exists": Object { + "field": "summary", + }, + }, + Object { + "range": Object { + "summary.down": Object { + "gt": "0", + }, }, }, Object { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts index ab21a12aa735..81a1e6ba7ca7 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts @@ -7,9 +7,13 @@ import { JsonObject } from '@kbn/utility-types'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { PromiseType } from 'utility-types'; import { asMutableArray } from '../../../common/utils/as_mutable_array'; import { UMElasticsearchQueryFn } from '../adapters'; import { Ping } from '../../../common/runtime_types/ping'; +import { createEsQuery } from '../../../common/utils/es_search'; +import { UptimeESClient } from '../lib'; +import { UNNAMED_LOCATION } from '../../../common/constants'; export interface GetMonitorStatusParams { filters?: JsonObject; @@ -41,24 +45,36 @@ const getLocationClause = (locations: string[]) => ({ export type AfterKey = Record | undefined; -export const getMonitorStatus: UMElasticsearchQueryFn< - GetMonitorStatusParams, - GetMonitorStatusResult[] -> = async ({ uptimeEsClient, filters, locations, numTimes, timespanRange, timestampRange }) => { - let afterKey: AfterKey; - - const STATUS = 'down'; - let monitors: any = []; - do { - // today this value is hardcoded. In the future we may support - // multiple status types for this alert, and this will become a parameter - const esParams = { +const executeQueryParams = async ({ + timestampRange, + timespanRange, + filters, + afterKey, + uptimeEsClient, + locations, +}: { + timespanRange: GetMonitorStatusParams['timespanRange']; + timestampRange: GetMonitorStatusParams['timestampRange']; + filters: GetMonitorStatusParams['filters']; + afterKey?: AfterKey; + uptimeEsClient: UptimeESClient; + locations: string[]; +}) => { + const queryParams = createEsQuery({ + body: { query: { bool: { filter: [ { - term: { - 'monitor.status': STATUS, + exists: { + field: 'summary', + }, + }, + { + range: { + 'summary.down': { + gt: '0', + }, }, }, { @@ -121,34 +137,65 @@ export const getMonitorStatus: UMElasticsearchQueryFn< fields: { top_hits: { size: 1, + sort: [{ '@timestamp': 'desc' }], }, }, }, }, }, - }; + }, + }); + + /** + * Perform a logical `and` against the selected location filters. + */ + if (locations.length) { + queryParams.body.query.bool.filter.push(getLocationClause(locations)); + } - /** - * Perform a logical `and` against the selected location filters. - */ - if (locations.length) { - esParams.query.bool.filter.push(getLocationClause(locations)); - } + const { body: result } = await uptimeEsClient.search(queryParams); + const afterKeyRes = result?.aggregations?.monitors?.after_key; + + const monitors = result?.aggregations?.monitors?.buckets || []; + + return { afterKeyRes, monitors }; +}; + +type QueryResponse = PromiseType>; + +export const getMonitorStatus: UMElasticsearchQueryFn< + GetMonitorStatusParams, + GetMonitorStatusResult[] +> = async ({ uptimeEsClient, filters, locations, numTimes, timespanRange, timestampRange }) => { + let afterKey: QueryResponse['afterKeyRes']; + + let monitors: QueryResponse['monitors'] = []; + + do { + // today this value is hardcoded. In the future we may support + // multiple status types for this alert, and this will become a parameter - const { body: result } = await uptimeEsClient.search({ - body: esParams, + const { afterKeyRes, monitors: monitorRes } = await executeQueryParams({ + afterKey, + timespanRange, + timestampRange, + filters, + uptimeEsClient, + locations, }); - afterKey = result?.aggregations?.monitors?.after_key as AfterKey; + afterKey = afterKeyRes; - monitors = monitors.concat(result?.aggregations?.monitors?.buckets || []); + monitors = monitors.concat(monitorRes); } while (afterKey !== undefined); return monitors - .filter((monitor: any) => monitor?.doc_count >= numTimes) - .map(({ key, doc_count: count, fields }: any) => ({ - ...key, + .filter((monitor) => monitor?.doc_count >= numTimes) + .map(({ key, doc_count: count, fields }) => ({ count, + monitorId: key.monitorId as string, + status: key.status as string, + location: key.location === null ? UNNAMED_LOCATION : (key.location as string), monitorInfo: fields?.hits?.hits?.[0]?._source, })); };