Skip to content

Commit

Permalink
Added index pattern validation
Browse files Browse the repository at this point in the history
  • Loading branch information
igoristic committed Jan 28, 2021
1 parent 1e9e71b commit 7cc3768
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 17 deletions.
3 changes: 2 additions & 1 deletion x-pack/plugins/monitoring/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,9 @@ export const ALERT_DETAILS = {
},
indexPattern: {
label: i18n.translate('xpack.monitoring.alerts.shardSize.paramDetails.indexPattern.label', {
defaultMessage: `Check for the following index patterns`,
defaultMessage: `Only check the following index patterns`,
}),
placeholder: 'eg: data-*, *prod-data, -.internal-data*',
type: AlertParamType.TextField,
},
},
Expand Down
57 changes: 57 additions & 0 deletions x-pack/plugins/monitoring/common/es_glob_patterns.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export interface RegExPatterns {
contains?: string | RegExp;
negate?: string | RegExp;
}

const valid = /.*/;

export class ESGlobPatterns {
public static createRegExPatterns(globPattern: string) {
if (globPattern === '*') {
return { contains: valid, negate: valid };
}

globPattern = globPattern.replace(/[ \\\/?"<>|#]/g, '');
const patternsArr = globPattern.split(',');
const containPatterns: string[] = [];
const negatePatterns: string[] = [];
patternsArr.forEach((pattern) => {
if (pattern.charAt(0) === '-') {
negatePatterns.push(ESGlobPatterns.createESGlobRegExStr(pattern.slice(1)));
} else {
containPatterns.push(ESGlobPatterns.createESGlobRegExStr(pattern));
}
});
const contains = containPatterns.length ? new RegExp(containPatterns.join('|'), 'gi') : valid;
const negate = negatePatterns.length
? new RegExp(`^((?!(${negatePatterns.join('|')})).)*$`, 'gi')
: valid;
return { contains, negate };
}

public static isValid(value: string, patterns: RegExPatterns) {
const { contains = valid, negate = valid } = patterns;
return new RegExp(contains).test(value) && new RegExp(negate).test(value);
}

private static createESGlobRegExStr(pattern: string) {
const patternsArr = pattern.split('*');
const firstItem = patternsArr.shift();
const lastItem = patternsArr.pop();
const start = firstItem?.length ? `(^${ESGlobPatterns.escapeStr(firstItem)})` : '';
const mid = patternsArr.map((group) => `(.*${ESGlobPatterns.escapeStr(group)})`);
const end = lastItem?.length ? `(.*${ESGlobPatterns.escapeStr(lastItem)}$)` : '';
const regExArr = ['(^', start, ...mid, end, ')'];
return regExArr.join('');
}

private static escapeStr(str: string) {
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
}
1 change: 1 addition & 0 deletions x-pack/plugins/monitoring/common/types/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface CommonAlertParams {
duration: string;
threshold?: number;
limit?: string;
[key: string]: unknown;
}

export interface ThreadPoolRejectionsAlertParams {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import React from 'react';
import { i18n } from '@kbn/i18n';
import { Expression, Props } from '../components/duration/expression';
import { Expression, Props } from '../components/param_details_form/expression';
import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public';
import { ALERT_CCR_READ_EXCEPTIONS, ALERT_DETAILS } from '../../../common/constants';
import { AlertTypeParams } from '../../../../alerts/common';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import React from 'react';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
import { ALERT_CPU_USAGE, ALERT_DETAILS } from '../../../common/constants';
import { validate, MonitoringAlertTypeParams } from '../components/duration/validation';
import { Expression, Props } from '../components/duration/expression';
import { validate, MonitoringAlertTypeParams } from '../components/param_details_form/validation';
import { Expression, Props } from '../components/param_details_form/expression';

export function createCpuUsageAlertType(): AlertTypeModel<MonitoringAlertTypeParams> {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
*/

import React from 'react';
import { validate, MonitoringAlertTypeParams } from '../components/duration/validation';
import { Expression, Props } from '../components/duration/expression';
import { validate, MonitoringAlertTypeParams } from '../components/param_details_form/validation';
import { Expression, Props } from '../components/param_details_form/expression';

// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import React from 'react';
import { i18n } from '@kbn/i18n';
import { Expression, Props } from '../components/duration/expression';
import { Expression, Props } from '../components/param_details_form/expression';
import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public';
import { ALERT_LARGE_SHARD_SIZE, ALERT_DETAILS } from '../../../common/constants';
import { AlertTypeParams } from '../../../../alerts/common';
Expand All @@ -20,9 +20,9 @@ const validate = (inputValues: ValidateOptions): ValidationResult => {
const errors: { [key: string]: string[] } = {
indexPattern: [],
};
if (!inputValues.duration) {
if (!inputValues.indexPattern) {
errors.indexPattern.push(
i18n.translate('xpack.monitoring.alerts.validation.duration', {
i18n.translate('xpack.monitoring.alerts.validation.indexPattern', {
defaultMessage: 'A valid index pattern/s is required.',
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
*/

import React from 'react';
import { validate, MonitoringAlertTypeParams } from '../components/duration/validation';
import { Expression, Props } from '../components/duration/expression';
import { validate, MonitoringAlertTypeParams } from '../components/param_details_form/validation';
import { Expression, Props } from '../components/param_details_form/expression';

// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiSpacer } from '@elastic/eui';
import { Expression, Props } from '../components/duration/expression';
import { Expression, Props } from '../components/param_details_form/expression';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
import { CommonAlertParamDetails } from '../../../common/types/alerts';
Expand Down
9 changes: 7 additions & 2 deletions x-pack/plugins/monitoring/server/alerts/base_alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ interface AlertOptions {
throttle?: string | null;
interval?: string;
legacy?: LegacyOptions;
defaultParams?: CommonAlertParams;
defaultParams?: Partial<CommonAlertParams>;
actionVariables: Array<{ name: string; description: string }>;
fetchClustersRange?: number;
accessorKey?: string;
Expand Down Expand Up @@ -89,7 +89,12 @@ export class BaseAlert {
public rawAlert?: SanitizedAlert,
public alertOptions: AlertOptions = defaultAlertOptions()
) {
this.alertOptions = { ...defaultAlertOptions(), ...this.alertOptions };
const defaultOptions = defaultAlertOptions();
defaultOptions.defaultParams = {
...defaultOptions.defaultParams,
...this.alertOptions.defaultParams,
};
this.alertOptions = { ...defaultOptions, ...this.alertOptions };
this.scopedLogger = Globals.app.getLogger(alertOptions.id!);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class LargeShardSizeAlert extends BaseAlert {
id: ALERT_LARGE_SHARD_SIZE,
name: ALERT_DETAILS[ALERT_LARGE_SHARD_SIZE].label,
throttle: '12h',
defaultParams: { indexPattern: '*' },
actionVariables: [
{
name: 'shardIndices',
Expand All @@ -64,14 +65,14 @@ export class LargeShardSizeAlert extends BaseAlert {
if (availableCcs) {
esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs);
}
const { threshold, indexPattern: shardIndexPattern } = params;
const { threshold, indexPattern: shardIndexPatterns } = params;

const stats = await fetchIndexShardSize(
callCluster,
clusters,
esIndexPattern,
threshold!,
shardIndexPattern,
shardIndexPatterns,
Globals.app.config.ui.max_bucket_size
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@

import { get } from 'lodash';
import { AlertCluster, IndexShardSizeStats } from '../../../common/types/alerts';
import { ESGlobPatterns, RegExPatterns } from '../../../common/es_glob_patterns';
import { Globals } from '../../static_globals';

const memoizedIndexPatterns = (globPatterns: string) => {
const createRegExPatterns = () => ESGlobPatterns.createRegExPatterns(globPatterns);
return Globals.app.getKeyStoreValue(
`large_shard_size_alert::${globPatterns}`,
createRegExPatterns
) as RegExPatterns;
};

export async function fetchIndexShardSize(
callCluster: any,
clusters: AlertCluster[],
index: string,
thresholdBytes: number,
shardIndexPattern: string,
shardIndexPatterns: string,
size: number
): Promise<IndexShardSizeStats[]> {
const params = {
Expand Down Expand Up @@ -90,6 +100,7 @@ export async function fetchIndexShardSize(
const response = await callCluster('search', params);
const stats: IndexShardSizeStats[] = [];
const { buckets: clusterBuckets = [] } = response.aggregations.clusters;
const validIndexPatterns = memoizedIndexPatterns(shardIndexPatterns);

if (!clusterBuckets.length) {
return stats;
Expand All @@ -101,6 +112,9 @@ export async function fetchIndexShardSize(

for (const indexBucket of indexBuckets) {
const shardIndex = indexBucket.key;
if (!ESGlobPatterns.isValid(shardIndex, validIndexPatterns)) {
continue;
}
const {
_index: monitoringIndexName,
_source: { source_node: sourceNode, index_stats: indexStats },
Expand Down
15 changes: 15 additions & 0 deletions x-pack/plugins/monitoring/server/static_globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,22 @@ interface IAppGlobals {
monitoringCluster: ILegacyCustomClusterClient;
config: MonitoringConfig;
getLogger: GetLogger;
getKeyStoreValue: (key: string, storeValueMethod?: () => unknown) => unknown;
}

interface KeyStoreData {
[key: string]: unknown;
}

const keyStoreData: KeyStoreData = {};
const getKeyStoreValue = (key: string, storeValueMethod?: () => unknown) => {
const value = keyStoreData[key];
if ((value === undefined || value == null) && typeof storeValueMethod === 'function') {
keyStoreData[key] = storeValueMethod();
}
return keyStoreData[key];
};

export class Globals {
private static _app: IAppGlobals;

Expand All @@ -37,6 +51,7 @@ export class Globals {
monitoringCluster,
config,
getLogger,
getKeyStoreValue,
};
}

Expand Down

0 comments on commit 7cc3768

Please sign in to comment.