From 8da466fa968e7aa42492c0b79fbd6dcd24288697 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Wed, 20 Sep 2023 12:00:55 +0200 Subject: [PATCH] added retry on failure --- .../synthetics/single_metric_config.ts | 10 ++- .../test_formula_metric_attribute.ts | 9 ++- .../common/constants/client_defaults.ts | 6 +- .../common/constants/monitor_defaults.ts | 1 + .../common/constants/monitor_management.ts | 1 + .../monitor_management/monitor_types.ts | 1 + .../monitor_types_project.ts | 1 + .../common/runtime_types/ping/ping.ts | 18 +++-- .../e2e/helpers/synthetics_runner.ts | 5 +- .../components/filter_status_button.tsx | 50 ++++++++++++++ .../hooks/use_error_failed_tests.tsx | 4 +- .../hooks/use_find_my_killer_state.ts | 4 +- .../hooks/use_last_error_state.tsx | 4 +- .../monitor_add_edit/form/field_config.tsx | 18 +++++ .../monitor_add_edit/form/form_config.tsx | 5 ++ .../components/monitor_add_edit/types.ts | 1 + .../hooks/use_monitor_errors.tsx | 4 +- .../hooks/use_monitor_pings.tsx | 15 +++- .../monitor_summary/status_filter.tsx | 42 ++++++++++++ .../monitor_summary/test_runs_table.tsx | 68 +++++++++++++------ .../test_runs_table_header.tsx | 33 ++++++++- .../hooks/use_simple_run_once_monitors.ts | 4 +- .../synthetics/hooks/use_last_x_checks.ts | 4 +- .../hooks/use_status_by_location.tsx | 4 +- .../state/monitor_details/actions.ts | 18 ++--- .../synthetics/state/monitor_details/api.ts | 24 ++++--- .../synthetics/state/monitor_details/index.ts | 11 +++ .../state/monitor_details/selectors.ts | 6 ++ x-pack/plugins/synthetics/scripts/base_e2e.js | 4 +- .../alert_rules/tls_rule/tls_rule_executor.ts | 4 +- .../server/common/pings/query_pings.ts | 2 + .../server/queries/query_monitor_status.ts | 4 +- .../monitor_cruds/monitor_validation.test.ts | 2 + .../server/routes/pings/get_pings.ts | 9 ++- .../migrations/monitors/8.8.0.test.ts | 1 + .../saved_objects/synthetics_monitor.ts | 1 + .../private_formatters/common_formatters.ts | 1 + .../private_location/clean_up_task.ts | 2 +- .../normalizers/common_fields.test.ts | 2 + .../normalizers/common_fields.ts | 14 ++++ 40 files changed, 337 insertions(+), 80 deletions(-) create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/filter_status_button.tsx create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/status_filter.tsx diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts index cae5b16a41584..9e65080e887fb 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts @@ -70,7 +70,10 @@ export function getSyntheticsSingleMetricConfig({ dataView }: ConfigProps): Seri }, titlePosition: 'bottom', }, - columnFilter: { language: 'kuery', query: 'summary.up: *' }, + columnFilter: { + language: 'kuery', + query: 'summary.final_attempt: true or ( not summary.final_attempt: * and summary:*)', + }, }, { id: 'monitor_duration', @@ -142,7 +145,10 @@ export function getSyntheticsSingleMetricConfig({ dataView }: ConfigProps): Seri }, field: RECORDS_FIELD, format: 'number', - columnFilter: { language: 'kuery', query: 'summary.down > 0' }, + columnFilter: { + language: 'kuery', + query: 'summary.status: down and summary.final_attempt: true', + }, }, ], labels: FieldLabels, diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/test_data/test_formula_metric_attribute.ts b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/test_data/test_formula_metric_attribute.ts index d1f63100ecbe8..39fb81bf72338 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/test_data/test_formula_metric_attribute.ts +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/test_data/test_formula_metric_attribute.ts @@ -40,7 +40,8 @@ export const sampleMetricFormulaAttribute = { dataType: 'number', filter: { language: 'kuery', - query: 'summary.up: *', + query: + 'summary.final_attempt: true or ( not summary.final_attempt: * and summary:*)', }, isBucketed: false, label: 'Availability', @@ -62,7 +63,8 @@ export const sampleMetricFormulaAttribute = { dataType: 'number', filter: { language: 'kuery', - query: '(summary.up: *) AND (summary.down > 0)', + query: + '(summary.final_attempt: true or ( not summary.final_attempt: * and summary:*)) AND (summary.down > 0)', }, isBucketed: false, label: 'Part of Availability', @@ -78,7 +80,8 @@ export const sampleMetricFormulaAttribute = { dataType: 'number', filter: { language: 'kuery', - query: 'summary.up: *', + query: + 'summary.final_attempt: true or ( not summary.final_attempt: * and summary:*)', }, isBucketed: false, label: 'Part of Availability', diff --git a/x-pack/plugins/synthetics/common/constants/client_defaults.ts b/x-pack/plugins/synthetics/common/constants/client_defaults.ts index 41a5f7c64abed..c330a928d8f93 100644 --- a/x-pack/plugins/synthetics/common/constants/client_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/client_defaults.ts @@ -45,9 +45,9 @@ export const CLIENT_DEFAULTS = { }; export const EXCLUDE_RUN_ONCE_FILTER = { bool: { must_not: { exists: { field: 'run_once' } } } }; -export const SUMMARY_FILTER = { - exists: { - field: 'summary', +export const FINAL_SUMMARY_FILTER = { + term: { + 'summary.final_attempt': true, }, }; diff --git a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts index 36f9fbf467dc0..e722801c12c1d 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts @@ -149,6 +149,7 @@ export const DEFAULT_COMMON_FIELDS: CommonFields = { [ConfigKey.CONFIG_HASH]: '', [ConfigKey.MONITOR_QUERY_ID]: '', [ConfigKey.PARAMS]: '', + [ConfigKey.MAX_ATTEMPTS]: 2, }; export const DEFAULT_BROWSER_ADVANCED_FIELDS: BrowserAdvancedFields = { diff --git a/x-pack/plugins/synthetics/common/constants/monitor_management.ts b/x-pack/plugins/synthetics/common/constants/monitor_management.ts index e87d05bc31521..a91e0132ff376 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_management.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_management.ts @@ -76,6 +76,7 @@ export enum ConfigKey { USERNAME = 'username', WAIT = 'wait', MONITOR_QUERY_ID = 'id', + MAX_ATTEMPTS = 'max_attempts', } export const secretKeys = [ diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts index af2304acb9e24..e109396965e7f 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts @@ -63,6 +63,7 @@ export const CommonFieldsCodec = t.intersection([ [ConfigKey.LOCATIONS]: MonitorLocationsCodec, [ConfigKey.MONITOR_QUERY_ID]: t.string, [ConfigKey.CONFIG_ID]: t.string, + [ConfigKey.MAX_ATTEMPTS]: t.number, }), t.partial({ [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorTypeCodec, diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts index 4a2a1a97ed88e..23e39660842fa 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types_project.ts @@ -47,6 +47,7 @@ export const ProjectMonitorCodec = t.intersection([ wait: t.string, hash: t.string, namespace: t.string, + retestOnFailure: t.boolean, }), ]); diff --git a/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts b/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts index 51adff582c478..12abb944e559f 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts @@ -143,6 +143,18 @@ export const UrlType = t.partial({ path: t.string, }); +const SummaryCodec = t.type({ + down: t.number, + up: t.number, + status: t.union([t.literal('up'), t.literal('down')]), + attempt: t.number, + max_attempts: t.number, + final_attempt: t.boolean, + retry_group: t.string, +}); + +export type TestSummary = t.TypeOf; + export const PingType = t.intersection([ t.type({ timestamp: t.string, @@ -205,10 +217,7 @@ export const PingType = t.intersection([ us: t.number, }), }), - summary: t.partial({ - down: t.number, - up: t.number, - }), + summary: SummaryCodec, synthetics: SyntheticsDataType, tags: t.array(t.string), tcp: t.partial({ @@ -292,6 +301,7 @@ export const GetPingsParamsType = t.intersection([ monitorId: t.string, sort: t.string, status: t.string, + finalAttempt: t.boolean, }), ]); diff --git a/x-pack/plugins/synthetics/e2e/helpers/synthetics_runner.ts b/x-pack/plugins/synthetics/e2e/helpers/synthetics_runner.ts index 3fb156a6ba0b3..dfaf7cc556d34 100644 --- a/x-pack/plugins/synthetics/e2e/helpers/synthetics_runner.ts +++ b/x-pack/plugins/synthetics/e2e/helpers/synthetics_runner.ts @@ -107,13 +107,14 @@ export class SyntheticsRunner { } const { headless, match, pauseOnError } = this.params; const noOfRuns = process.env.NO_OF_RUNS ? Number(process.env.NO_OF_RUNS) : 1; + const CI = process.env.CI === 'true'; console.log(`Running ${noOfRuns} times`); let results: PromiseType> = {}; for (let i = 0; i < noOfRuns; i++) { results = await syntheticsRun({ params: { kibanaUrl: this.kibanaUrl, getService: this.getService }, playwrightOptions: { - headless, + headless: headless ?? !CI, chromiumSandbox: false, timeout: 60 * 1000, viewport: { @@ -125,7 +126,7 @@ export class SyntheticsRunner { }, }, match: match === 'undefined' ? '' : match, - pauseOnError, + pauseOnError: pauseOnError ?? !CI, screenshots: 'only-on-failure', reporter: TestReporter, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/filter_status_button.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/filter_status_button.tsx new file mode 100644 index 0000000000000..7615f8aaa52a0 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/filter_status_button.tsx @@ -0,0 +1,50 @@ +/* + * 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 { EuiFilterButton, EuiButtonColor } from '@elastic/eui'; +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { selectStatusFilter, setStatusFilter } from '../../../state'; + +export interface FilterStatusButtonProps { + content: string | JSX.Element; + dataTestSubj: string; + isDisabled?: boolean; + value?: 'up' | 'down'; + withNext: boolean; +} + +export const FilterStatusButton = ({ + content, + dataTestSubj, + isDisabled, + value, + withNext, +}: FilterStatusButtonProps) => { + const statusFilter = useSelector(selectStatusFilter); + const dispatch = useDispatch(); + + const isActive = statusFilter === value; + let color: EuiButtonColor = 'text'; + if (isActive) { + color = value === 'up' ? 'success' : value === 'down' ? 'danger' : 'text'; + } + return ( + { + dispatch(setStatusFilter(statusFilter === value ? undefined : value)); + }} + withNext={withNext} + color={color} + > + {content} + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx index a61bbb9d2b6b0..eb5268c506089 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_error_failed_tests.tsx @@ -11,7 +11,7 @@ import { useMemo } from 'react'; import { Ping } from '../../../../../../common/runtime_types'; import { EXCLUDE_RUN_ONCE_FILTER, - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, } from '../../../../../../common/constants/client_defaults'; import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; import { useSyntheticsRefreshContext } from '../../../contexts'; @@ -32,7 +32,7 @@ export function useErrorFailedTests() { query: { bool: { filter: [ - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, { term: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_find_my_killer_state.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_find_my_killer_state.ts index 33f923c94d2ba..88e23cb6dcfae 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_find_my_killer_state.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_find_my_killer_state.ts @@ -11,7 +11,7 @@ import { useReduxEsSearch } from '../../../hooks/use_redux_es_search'; import { Ping } from '../../../../../../common/runtime_types'; import { EXCLUDE_RUN_ONCE_FILTER, - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, } from '../../../../../../common/constants/client_defaults'; import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; import { useSyntheticsRefreshContext } from '../../../contexts'; @@ -39,7 +39,7 @@ export function useFindMyKillerState() { query: { bool: { filter: [ - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, { term: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_last_error_state.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_last_error_state.tsx index c0f100a3441ca..d86b41f08f5bb 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_last_error_state.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/hooks/use_last_error_state.tsx @@ -11,7 +11,7 @@ import { useReduxEsSearch } from '../../../hooks/use_redux_es_search'; import { Ping } from '../../../../../../common/runtime_types'; import { EXCLUDE_RUN_ONCE_FILTER, - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, } from '../../../../../../common/constants/client_defaults'; import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; import { useSyntheticsRefreshContext } from '../../../contexts'; @@ -32,7 +32,7 @@ export function useErrorFailedTests() { query: { bool: { filter: [ - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, { term: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx index a7ab9c4e7d56f..1fb9eae1fe7b7 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx @@ -1589,4 +1589,22 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ }, }), }, + [ConfigKey.MAX_ATTEMPTS]: { + fieldKey: ConfigKey.MAX_ATTEMPTS, + component: Switch, + controlled: true, + props: ({ setValue, field, trigger }): EuiSwitchProps => ({ + id: 'syntheticsMonitorConfigMaxAttempts', + label: i18n.translate('xpack.synthetics.monitorConfig.retest.label', { + defaultMessage: 'Enable retest on failure', + }), + checked: field?.value === 2, + onChange: async (event) => { + const isChecked = !!event.target.checked; + setValue(ConfigKey.MAX_ATTEMPTS, isChecked ? 2 : 1); + await trigger(ConfigKey.MAX_ATTEMPTS); + }, + 'data-test-subj': 'syntheticsEnableAttemptSwitch', + }), + }, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx index 8e44f9727a5c3..ea7653130759d 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx @@ -198,6 +198,7 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.MAX_REDIRECTS], FIELD(readOnly)[ConfigKey.TIMEOUT], FIELD(readOnly)[ConfigKey.ENABLED], + FIELD(readOnly)[ConfigKey.MAX_ATTEMPTS], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], FIELD(readOnly)[AlertConfigKey.TLS_ENABLED], ], @@ -218,6 +219,7 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[`${ConfigKey.SCHEDULE}.number`], FIELD(readOnly)[ConfigKey.TIMEOUT], FIELD(readOnly)[ConfigKey.ENABLED], + FIELD(readOnly)[ConfigKey.MAX_ATTEMPTS], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], FIELD(readOnly)[AlertConfigKey.TLS_ENABLED], ], @@ -235,6 +237,7 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.LOCATIONS], FIELD(readOnly)[`${ConfigKey.SCHEDULE}.number`], FIELD(readOnly)[ConfigKey.ENABLED], + FIELD(readOnly)[ConfigKey.MAX_ATTEMPTS], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], ], step3: [FIELD(readOnly)['source.inline'], FIELD(readOnly)[ConfigKey.PARAMS]], @@ -261,6 +264,7 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.LOCATIONS], FIELD(readOnly)[`${ConfigKey.SCHEDULE}.number`], FIELD(readOnly)[ConfigKey.ENABLED], + FIELD(readOnly)[ConfigKey.MAX_ATTEMPTS], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], ], advanced: [ @@ -286,6 +290,7 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.WAIT], FIELD(readOnly)[ConfigKey.TIMEOUT], FIELD(readOnly)[ConfigKey.ENABLED], + FIELD(readOnly)[ConfigKey.MAX_ATTEMPTS], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], ], advanced: [DEFAULT_DATA_OPTIONS(readOnly), ICMP_ADVANCED(readOnly).requestConfig], diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts index d318fa878e9cf..e779416014396 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts @@ -163,4 +163,5 @@ export interface FieldMap { [ConfigKey.IGNORE_HTTPS_ERRORS]: FieldMeta; [ConfigKey.MODE]: FieldMeta; [ConfigKey.IPV4]: FieldMeta; + [ConfigKey.MAX_ATTEMPTS]: FieldMeta; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx index 969e98a21c36c..69871de54c7af 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx @@ -11,7 +11,7 @@ import { useSelectedLocation } from './use_selected_location'; import { Ping, PingState } from '../../../../../../common/runtime_types'; import { EXCLUDE_RUN_ONCE_FILTER, - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, } from '../../../../../../common/constants/client_defaults'; import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; import { useSyntheticsRefreshContext } from '../../../contexts'; @@ -37,7 +37,7 @@ export function useMonitorErrors(monitorIdArg?: string) { query: { bool: { filter: [ - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, { range: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_pings.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_pings.tsx index 59460fa814969..aad04f78f361c 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_pings.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_pings.tsx @@ -10,7 +10,12 @@ import { useDispatch, useSelector } from 'react-redux'; import { useSelectedMonitor } from './use_selected_monitor'; import { useSelectedLocation } from './use_selected_location'; -import { getMonitorRecentPingsAction, selectMonitorPingsMetadata } from '../../../state'; +import { + getMonitorRecentPingsAction, + selectMonitorPingsMetadata, + selectShowOnlyFinalAttempts, + selectStatusFilter, +} from '../../../state'; interface UseMonitorPingsProps { lastRefresh?: number; @@ -29,6 +34,10 @@ export const useMonitorPings = (props?: UseMonitorPingsProps) => { const monitorId = monitor?.id; const locationLabel = location?.label; + const showOnlyFinalAttempts = useSelector(selectShowOnlyFinalAttempts); + + const statusFilter = useSelector(selectStatusFilter); + useEffect(() => { if (monitorId && locationLabel) { dispatch( @@ -39,6 +48,8 @@ export const useMonitorPings = (props?: UseMonitorPingsProps) => { pageIndex: props?.pageIndex, from: props?.from, to: props?.to, + finalAttempt: showOnlyFinalAttempts, + statusFilter, }) ); } @@ -51,6 +62,8 @@ export const useMonitorPings = (props?: UseMonitorPingsProps) => { props?.pageIndex, props?.from, props?.to, + showOnlyFinalAttempts, + statusFilter, ]); const { total, data: pings, loading } = useSelector(selectMonitorPingsMetadata); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/status_filter.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/status_filter.tsx new file mode 100644 index 0000000000000..5dcd46d182cc4 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/status_filter.tsx @@ -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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFilterGroup } from '@elastic/eui'; +import { FilterStatusButton } from '../../common/components/filter_status_button'; +import { + STATUS_DOWN_LABEL, + STATUS_UP_LABEL, +} from '../../../../../../common/translations/translations'; + +export const StatusFilter: React.FC = () => { + return ( + + + + + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx index 0bbbd3a5f247b..cf4e5532519ed 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table.tsx @@ -15,12 +15,14 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, + EuiIconTip, EuiPanel, EuiText, useIsWithinMinBreakpoint, } from '@elastic/eui'; import { Criteria } from '@elastic/eui/src/components/basic_table/basic_table'; import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; +import { css } from '@kbn/kibana-react-plugin/common'; import { INSPECT_DOCUMENT, ViewDocument } from '../../common/components/view_document'; import { ExpandRowColumn, @@ -162,33 +164,53 @@ export const TestRunsTable = ({ ), }, }, + { + align: 'left', + valign: 'middle', + field: 'monitor.status', + name: RESULT_LABEL, + sortable: true, + render: (status: string, test: Ping) => { + const attemptNo = test.summary?.attempt ?? 1; + const isFinalAttempt = test.summary?.final_attempt ?? false; + if (!isFinalAttempt || attemptNo === 1) { + return ; + } + return ( + + + + + + + + + ); + }, + mobileOptions: { + show: false, + }, + }, ...(!isBrowserMonitor ? [ { align: 'left', field: 'monitor.ip', + sortable: true, name: i18n.translate('xpack.synthetics.pingList.ipAddressColumnLabel', { defaultMessage: 'IP', }), }, ] : []), - { - align: 'left', - valign: 'middle', - field: 'monitor.status', - name: RESULT_LABEL, - sortable: true, - render: (status: string) => , - mobileOptions: { - show: false, - }, - }, { align: 'left', field: 'error.message', name: MESSAGE_LABEL, textOnly: true, + css: css` + max-width: 600px; + `, render: (errorMessage: string) => ( {errorMessage?.length > 0 ? errorMessage : '-'} ), @@ -290,15 +312,7 @@ export const TestRunsTable = ({ columns={columns} error={pingsError?.body?.message} items={sortedPings} - noItemsMessage={ - pingsLoading - ? i18n.translate('xpack.synthetics.monitorDetails.loadingTestRuns', { - defaultMessage: 'Loading test runs...', - }) - : i18n.translate('xpack.synthetics.monitorDetails.noDataFound', { - defaultMessage: 'No data found', - }) - } + noItemsMessage={pingsLoading ? LOADING_TEST_RUNS : NO_DATA_FOUND} tableLayout={'auto'} sorting={sorting} onChange={handleTableChange} @@ -309,7 +323,7 @@ export const TestRunsTable = ({ pageIndex: page.index, pageSize: page.size, totalItemCount: total, - pageSizeOptions: [10, 20, 50], // TODO Confirm with Henry, + pageSizeOptions: [5, 10, 20, 50], } : undefined } @@ -400,6 +414,10 @@ const SCREENSHOT_LABEL = i18n.translate('xpack.synthetics.monitorDetails.summary defaultMessage: 'Screenshot', }); +const FINAL_ATTEMPT_LABEL = i18n.translate('xpack.synthetics.monitorDetails.summary.finalAttempt', { + defaultMessage: 'This is a retest since retry on failure is enabled.', +}); + const RESULT_LABEL = i18n.translate('xpack.synthetics.monitorDetails.summary.result', { defaultMessage: 'Result', }); @@ -411,3 +429,11 @@ const MESSAGE_LABEL = i18n.translate('xpack.synthetics.monitorDetails.summary.me const DURATION_LABEL = i18n.translate('xpack.synthetics.monitorDetails.summary.duration', { defaultMessage: 'Duration', }); + +const LOADING_TEST_RUNS = i18n.translate('xpack.synthetics.monitorDetails.loadingTestRuns', { + defaultMessage: 'Loading test runs...', +}); + +const NO_DATA_FOUND = i18n.translate('xpack.synthetics.monitorDetails.noDataFound', { + defaultMessage: 'No data found', +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx index 6785ed7f1ddc8..3e43f23956180 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx @@ -8,8 +8,18 @@ import React from 'react'; import { useHistory } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiLink, EuiTitle } from '@elastic/eui'; +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSwitch, + EuiTitle, +} from '@elastic/eui'; +import { useDispatch, useSelector } from 'react-redux'; +import { selectShowOnlyFinalAttempts, showOnlyFinalAttemptsAction } from '../../../state'; +import { StatusFilter } from './status_filter'; import { MONITOR_HISTORY_ROUTE } from '../../../../../../common/constants'; import { ConfigKey, Ping } from '../../../../../../common/runtime_types'; import { useGetUrlParams } from '../../../hooks'; @@ -33,14 +43,29 @@ export const TestRunsTableHeader = ({ const { monitor } = useSelectedMonitor(); + const showOnlyFinalAttempts = useSelector(selectShowOnlyFinalAttempts); + + const dispatch = useDispatch(); + return ( - +

{paginable || pings?.length < 10 ? TEST_RUNS : LAST_10_TEST_RUNS}

+ + + + + dispatch(showOnlyFinalAttemptsAction(e.target.checked))} + /> + {showViewHistoryButton ? ( ({ query: { bool: { filter: [ - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, getTimeRangeFilter(schedule), { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_status_by_location.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_status_by_location.tsx index f3110999286a3..7d06f04130c05 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_status_by_location.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_status_by_location.tsx @@ -11,7 +11,7 @@ import { useLocations } from './use_locations'; import { EncryptedSyntheticsSavedMonitor, Ping } from '../../../../common/runtime_types'; import { EXCLUDE_RUN_ONCE_FILTER, - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, } from '../../../../common/constants/client_defaults'; import { SYNTHETICS_INDEX_PATTERN, UNNAMED_LOCATION } from '../../../../common/constants'; import { useSyntheticsRefreshContext } from '../contexts'; @@ -39,7 +39,7 @@ export function useStatusByLocation({ query: { bool: { filter: [ - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, EXCLUDE_RUN_ONCE_FILTER, { term: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts index 195c2b1fcb400..b13233f5f2282 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts @@ -6,6 +6,7 @@ */ import { createAction } from '@reduxjs/toolkit'; +import { MostRecentPingsRequest } from './api'; import { Ping, PingsResponse, @@ -33,14 +34,9 @@ export const updateMonitorLastRunAction = createAction<{ data: Ping }>( '[MONITOR DETAILS] UPdATE LAST RUN' ); -export const getMonitorRecentPingsAction = createAsyncAction< - { - monitorId: string; - locationId: string; - size?: number; - pageIndex?: number; - from?: string; - to?: string; - }, - PingsResponse ->('[MONITOR DETAILS] GET RECENT PINGS'); +export const getMonitorRecentPingsAction = createAsyncAction( + '[MONITOR DETAILS] GET RECENT PINGS' +); + +export const showOnlyFinalAttemptsAction = createAction('SHOW ONLY FINAL ATTEMPTS'); +export const setStatusFilter = createAction<'up' | 'down' | undefined>('SET STATUS FILTER'); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts index 2f6e8d2df069e..fb7953dba9cd6 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts @@ -25,6 +25,17 @@ export const fetchMonitorLastRun = async ({ return fetchMonitorRecentPings({ monitorId, locationId, size: 1 }); }; +export interface MostRecentPingsRequest { + monitorId: string; + locationId: string; + from?: string; + to?: string; + size?: number; + pageIndex?: number; + finalAttempt?: boolean; + statusFilter?: 'up' | 'down'; +} + export const fetchMonitorRecentPings = async ({ monitorId, locationId, @@ -32,14 +43,9 @@ export const fetchMonitorRecentPings = async ({ to, size = 10, pageIndex = 0, -}: { - monitorId: string; - locationId: string; - from?: string; - to?: string; - size?: number; - pageIndex?: number; -}): Promise => { + finalAttempt, + statusFilter, +}: MostRecentPingsRequest): Promise => { const locations = JSON.stringify([locationId]); const sort = 'desc'; @@ -53,6 +59,8 @@ export const fetchMonitorRecentPings = async ({ sort, size, pageIndex, + finalAttempt, + status: statusFilter, }, PingsResponseType ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts index af0ccf9046eea..cf3fff3c428d7 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts @@ -19,6 +19,8 @@ import { getMonitorRecentPingsAction, setMonitorDetailsLocationAction, getMonitorAction, + showOnlyFinalAttemptsAction, + setStatusFilter, } from './actions'; export interface MonitorDetailsState { @@ -37,6 +39,8 @@ export interface MonitorDetailsState { syntheticsMonitorDispatchedAt: number; error: IHttpSerializedFetchError | null; selectedLocationId: string | null; + showOnlyFinalAttempts?: boolean; + statusFilter?: 'up' | 'down' | undefined; } const initialState: MonitorDetailsState = { @@ -47,6 +51,7 @@ const initialState: MonitorDetailsState = { syntheticsMonitorDispatchedAt: 0, error: null, selectedLocationId: null, + showOnlyFinalAttempts: false, }; export const monitorDetailsReducer = createReducer(initialState, (builder) => { @@ -110,6 +115,12 @@ export const monitorDetailsReducer = createReducer(initialState, (builder) => { if ('updated_at' in action.payload && state.syntheticsMonitor) { state.syntheticsMonitor = action.payload; } + }) + .addCase(showOnlyFinalAttemptsAction, (state, action) => { + state.showOnlyFinalAttempts = action.payload; + }) + .addCase(setStatusFilter, (state, action) => { + state.statusFilter = action.payload; }); }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts index b4aa80c558e8a..c87e8b6775c6b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts @@ -25,3 +25,9 @@ export const selectPingsLoading = createSelector(getState, (state) => state.ping export const selectMonitorPingsMetadata = createSelector(getState, (state) => state.pings); export const selectPingsError = createSelector(getState, (state) => state.error); +export const selectShowOnlyFinalAttempts = createSelector( + getState, + (state) => state.showOnlyFinalAttempts ?? false +); + +export const selectStatusFilter = createSelector(getState, (state) => state.statusFilter); diff --git a/x-pack/plugins/synthetics/scripts/base_e2e.js b/x-pack/plugins/synthetics/scripts/base_e2e.js index 99fb58cf81d6a..9cf6afcb2eb73 100644 --- a/x-pack/plugins/synthetics/scripts/base_e2e.js +++ b/x-pack/plugins/synthetics/scripts/base_e2e.js @@ -23,7 +23,7 @@ const { argv } = yargs(process.argv.slice(2)) 'Run all tests (an instance of Elasticsearch and kibana are needs to be available)', }) .option('pauseOnError', { - default: false, + default: !Boolean(process.env.CI), type: 'boolean', description: 'Pause the Synthetics Test Runner on error', }) @@ -33,7 +33,7 @@ const { argv } = yargs(process.argv.slice(2)) description: 'Path to the Kibana install directory', }) .option('headless', { - default: true, + default: Boolean(process.env.CI), type: 'boolean', description: 'Start in headless mode', }) diff --git a/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts b/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts index 0ca15a53efc0a..e2f9f4c95cc63 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/tls_rule/tls_rule_executor.ts @@ -11,7 +11,7 @@ import { import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import moment from 'moment'; -import { SUMMARY_FILTER } from '../../../common/constants/client_defaults'; +import { FINAL_SUMMARY_FILTER } from '../../../common/constants/client_defaults'; import { formatFilterString } from '../common'; import { SyntheticsServerSetup } from '../../types'; import { getSyntheticsCerts } from '../../queries/get_certs'; @@ -188,7 +188,7 @@ export class TLSRuleExecutor { config_id: configIds, }, }, - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, ], must_not: { bool: { diff --git a/x-pack/plugins/synthetics/server/common/pings/query_pings.ts b/x-pack/plugins/synthetics/server/common/pings/query_pings.ts index 23c4ce5357819..762233103e871 100644 --- a/x-pack/plugins/synthetics/server/common/pings/query_pings.ts +++ b/x-pack/plugins/synthetics/server/common/pings/query_pings.ts @@ -94,6 +94,7 @@ export async function queryPings( pageIndex, locations, excludedLocations, + finalAttempt, } = params; const size = sizeParam ?? DEFAULT_PAGE_SIZE; @@ -107,6 +108,7 @@ export async function queryPings( { range: { '@timestamp': { gte: from, lte: to } } }, ...(monitorId ? [{ term: { 'monitor.id': monitorId } }] : []), ...(status ? [{ term: { 'monitor.status': status } }] : []), + ...(finalAttempt ? [{ term: { 'summary.final_attempt': finalAttempt } }] : []), ] as QueryDslQueryContainer[], ...REMOVE_NON_SUMMARY_BROWSER_CHECKS, }, diff --git a/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts b/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts index e18e4f23b34c8..ddea241f3a3df 100644 --- a/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts +++ b/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts @@ -9,7 +9,7 @@ import pMap from 'p-map'; import times from 'lodash/times'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { cloneDeep, intersection } from 'lodash'; -import { SUMMARY_FILTER } from '../../common/constants/client_defaults'; +import { FINAL_SUMMARY_FILTER } from '../../common/constants/client_defaults'; import { OverviewPendingStatusMetaData, OverviewPing, @@ -69,7 +69,7 @@ export async function queryMonitorStatus( query: { bool: { filter: [ - SUMMARY_FILTER, + FINAL_SUMMARY_FILTER, { range: { '@timestamp': { diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts index 7c2498242b35c..7d75a9644004d 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts @@ -76,6 +76,7 @@ describe('validateMonitor', () => { ], [ConfigKey.NAMESPACE]: 'testnamespace', [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.MULTISTEP, + [ConfigKey.MAX_ATTEMPTS]: 2, }; testMetaData = { is_tls_enabled: false, @@ -477,6 +478,7 @@ function getJsonPayload() { ' }' + ' },' + ' "max_redirects": "3",' + + ' "max_attempts": 2,' + ' "password": "test",' + ' "urls": "https://nextjs-test-synthetics.vercel.app/api/users",' + ' "proxy_url": "http://proxy.com",' + diff --git a/x-pack/plugins/synthetics/server/routes/pings/get_pings.ts b/x-pack/plugins/synthetics/server/routes/pings/get_pings.ts index 156a2e5dd62c8..8b464ef887a3c 100644 --- a/x-pack/plugins/synthetics/server/routes/pings/get_pings.ts +++ b/x-pack/plugins/synthetics/server/routes/pings/get_pings.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; +import { schema, TypeOf } from '@kbn/config-schema'; import { SyntheticsRestApiRouteFactory } from '../types'; import { SYNTHETICS_API_URLS } from '../../../common/constants'; import { queryPings } from '../../common/pings/query_pings'; @@ -21,8 +21,11 @@ export const getPingsRouteQuerySchema = schema.object({ pageIndex: schema.maybe(schema.number()), sort: schema.maybe(schema.string()), status: schema.maybe(schema.string()), + finalAttempt: schema.maybe(schema.boolean()), }); +type GetPingsRouteRequest = TypeOf; + export const syntheticsGetPingsRoute: SyntheticsRestApiRouteFactory = () => ({ method: 'GET', path: SYNTHETICS_API_URLS.PINGS, @@ -41,7 +44,8 @@ export const syntheticsGetPingsRoute: SyntheticsRestApiRouteFactory = () => ({ pageIndex, locations, excludedLocations, - } = request.query; + finalAttempt, + } = request.query as GetPingsRouteRequest; return await queryPings({ uptimeEsClient, @@ -54,6 +58,7 @@ export const syntheticsGetPingsRoute: SyntheticsRestApiRouteFactory = () => ({ pageIndex, locations: locations ? JSON.parse(locations) : [], excludedLocations, + finalAttempt, }); }, }); diff --git a/x-pack/plugins/synthetics/server/saved_objects/migrations/monitors/8.8.0.test.ts b/x-pack/plugins/synthetics/server/saved_objects/migrations/monitors/8.8.0.test.ts index 8f3b5e3c85a64..f816c2c2e2c83 100644 --- a/x-pack/plugins/synthetics/server/saved_objects/migrations/monitors/8.8.0.test.ts +++ b/x-pack/plugins/synthetics/server/saved_objects/migrations/monitors/8.8.0.test.ts @@ -137,6 +137,7 @@ describe('Monitor migrations v8.7.0 -> v8.8.0', () => { label: 'North America - US Central', }, ], + max_attempts: 2, name: 'https://elastic.co', namespace: 'default', origin: 'ui', diff --git a/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor.ts b/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor.ts index d206cd73285ea..3a1ea76d671f5 100644 --- a/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor.ts +++ b/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor.ts @@ -44,6 +44,7 @@ export const SYNTHETICS_MONITOR_ENCRYPTED_TYPE = { attributesToExcludeFromAAD: new Set([ ConfigKey.ALERT_CONFIG, ConfigKey.METADATA, + ConfigKey.MAX_ATTEMPTS, ...legacyConfigKeys, ]), }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/common_formatters.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/common_formatters.ts index b8d2687c34a8e..ed55403a660b9 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/common_formatters.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/common_formatters.ts @@ -36,6 +36,7 @@ export const commonFormatters: CommonFormatMap = { [ConfigKey.CONFIG_HASH]: null, [ConfigKey.MONITOR_QUERY_ID]: stringToJsonFormatter, [ConfigKey.PARAMS]: null, + [ConfigKey.MAX_ATTEMPTS]: null, [ConfigKey.SCHEDULE]: (fields) => JSON.stringify( `@every ${fields[ConfigKey.SCHEDULE]?.number}${fields[ConfigKey.SCHEDULE]?.unit}` diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/clean_up_task.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/clean_up_task.ts index 7fdc4b7fa1605..5bc47c17ef26e 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/clean_up_task.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/clean_up_task.ts @@ -84,7 +84,7 @@ export const registerCleanUpTask = ( if (remaining.length === 0) { return { state, schedule: { interval: '24h' } }; } else { - return { state, schedule: { interval: '15m' } }; + return { state, schedule: { interval: '20m' } }; } } } catch (e) { diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts index f86c3097e809c..6c381283c1824 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts @@ -136,6 +136,7 @@ describe('getNormalizeCommonFields', () => { tags: [], timeout: '16', params: '', + max_attempts: 2, }, }); } @@ -200,6 +201,7 @@ describe('getNormalizeCommonFields', () => { tags: [], timeout: '16', params: '', + max_attempts: 2, }, }); }); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts index 61509249e6ec1..1842c6bf8df7f 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts @@ -84,6 +84,7 @@ export const getNormalizeCommonFields = ({ ? getValueInSeconds(monitor.timeout) : defaultFields[ConfigKey.TIMEOUT], [ConfigKey.CONFIG_HASH]: monitor.hash || defaultFields[ConfigKey.CONFIG_HASH], + [ConfigKey.MAX_ATTEMPTS]: getMaxAttempts(monitor), [ConfigKey.PARAMS]: Object.keys(monitor.params || {}).length ? JSON.stringify(monitor.params) : defaultFields[ConfigKey.PARAMS], @@ -117,6 +118,19 @@ const getAlertConfig = (monitor: ProjectMonitor) => { : defaultFields[ConfigKey.ALERT_CONFIG]; }; +const ONL_ONE_ATTEMPT = 1; + +const getMaxAttempts = (monitor: ProjectMonitor) => { + const defaultFields = DEFAULT_COMMON_FIELDS; + const retestOnFailure = monitor.retestOnFailure; + if (retestOnFailure) { + return defaultFields[ConfigKey.MAX_ATTEMPTS]; + } else if (monitor.retestOnFailure === false) { + return ONL_ONE_ATTEMPT; + } + return defaultFields[ConfigKey.MAX_ATTEMPTS]; +}; + export const getCustomHeartbeatId = ( monitor: NormalizedProjectProps['monitor'], projectId: string,