Skip to content

Commit

Permalink
[ResponseOps][Rules] Move metric rule params schema to package (elast…
Browse files Browse the repository at this point in the history
…ic#205492)

Connected with elastic#195189

## Summary

- Moved params of duration metric inventory threshold rule type to
`/response-ops/rule_params/metric_inventory_threshold/`
- Moved params of metric threshold rule type to
`/response-ops/rule_params/metric_threshold/`

**I did NOT move the corresponding type to the rule_params package due
to the recursive imports it would create.**

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
adcoelho and kibanamachine authored Jan 8, 2025
1 parent bc5d8db commit 4873fa1
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 146 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { schema } from '@kbn/config-schema';
import { isEmpty } from 'lodash';

export const oneOfLiterals = (arrayOfLiterals: Readonly<string[]>) =>
schema.string({
validate: (value) =>
arrayOfLiterals.includes(value) ? undefined : `must be one of ${arrayOfLiterals.join(' | ')}`,
});

export const validateIsStringElasticsearchJSONFilter = (value: string) => {
if (value === '') {
// Allow clearing the filter.
return;
}

const errorMessage = 'filterQuery must be a valid Elasticsearch filter expressed in JSON';
try {
const parsedValue = JSON.parse(value);
if (!isEmpty(parsedValue.bool)) {
return undefined;
}
return errorMessage;
} catch (e) {
return errorMessage;
}
};

export type TimeUnitChar = 's' | 'm' | 'h' | 'd';

export enum LEGACY_COMPARATORS {
OUTSIDE_RANGE = 'outside',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export { metricInventoryThresholdRuleParamsSchema } from './latest';
export { metricInventoryThresholdRuleParamsSchema as metricInventoryThresholdRuleParamsSchemaV1 } from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export * from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { schema, Type } from '@kbn/config-schema';
import { COMPARATORS } from '@kbn/alerting-comparators';

import {
LEGACY_COMPARATORS,
TimeUnitChar,
oneOfLiterals,
validateIsStringElasticsearchJSONFilter,
} from '../common/utils';

const SNAPSHOT_CUSTOM_AGGREGATIONS = ['avg', 'max', 'min', 'rate'] as const;
type SnapshotCustomAggregation = (typeof SNAPSHOT_CUSTOM_AGGREGATIONS)[number];

const SnapshotMetricTypeKeysArray = [
'count',
'cpuV2',
'cpu',
'diskLatency',
'diskSpaceUsage',
'load',
'memory',
'memoryFree',
'memoryTotal',
'normalizedLoad1m',
'tx',
'rx',
'txV2',
'rxV2',
'logRate',
'diskIOReadBytes',
'diskIOWriteBytes',
's3TotalRequests',
's3NumberOfObjects',
's3BucketSize',
's3DownloadBytes',
's3UploadBytes',
'rdsConnections',
'rdsQueriesExecuted',
'rdsActiveTransactions',
'rdsLatency',
'sqsMessagesVisible',
'sqsMessagesDelayed',
'sqsMessagesSent',
'sqsMessagesEmpty',
'sqsOldestMessage',
'custom',
];
type SnapshotMetricTypeKeys = (typeof SNAPSHOT_CUSTOM_AGGREGATIONS)[number];

const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS });

export const metricInventoryThresholdRuleParamsSchema = schema.object(
{
criteria: schema.arrayOf(
schema.object({
threshold: schema.arrayOf(schema.number()),
comparator: oneOfLiterals(comparators) as Type<COMPARATORS>,
timeUnit: schema.string() as Type<TimeUnitChar>,
timeSize: schema.number(),
metric: oneOfLiterals(SnapshotMetricTypeKeysArray) as Type<SnapshotMetricTypeKeys>,
warningThreshold: schema.maybe(schema.arrayOf(schema.number())),
warningComparator: schema.maybe(oneOfLiterals(comparators)),
customMetric: schema.maybe(
schema.object({
type: schema.literal('custom'),
id: schema.string(),
field: schema.string(),
aggregation: oneOfLiterals(
SNAPSHOT_CUSTOM_AGGREGATIONS
) as Type<SnapshotCustomAggregation>,
label: schema.maybe(schema.string()),
})
),
})
),
nodeType: schema.string(),
filterQuery: schema.maybe(schema.string({ validate: validateIsStringElasticsearchJSONFilter })),
sourceId: schema.string(),
alertOnNoData: schema.maybe(schema.boolean()),
},
{ unknowns: 'allow' }
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export { metricThresholdRuleParamsSchema } from './latest';
export { metricThresholdRuleParamsSchema as metricThresholdRuleParamsSchemaV1 } from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export * from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { schema } from '@kbn/config-schema';
import { COMPARATORS } from '@kbn/alerting-comparators';

import {
LEGACY_COMPARATORS,
oneOfLiterals,
validateIsStringElasticsearchJSONFilter,
} from '../common/utils';

const METRIC_EXPLORER_AGGREGATIONS = [
'avg',
'max',
'min',
'cardinality',
'rate',
'count',
'sum',
'p95',
'p99',
'custom',
] as const;

const comparator = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS });

const baseCriterion = {
threshold: schema.arrayOf(schema.number()),
comparator: oneOfLiterals(comparator),
timeUnit: schema.string(),
timeSize: schema.number(),
warningThreshold: schema.maybe(schema.arrayOf(schema.number())),
warningComparator: schema.maybe(oneOfLiterals(comparator)),
};

const nonCountCriterion = schema.object({
...baseCriterion,
metric: schema.string(),
aggType: oneOfLiterals(METRIC_EXPLORER_AGGREGATIONS),
customMetrics: schema.never(),
equation: schema.never(),
label: schema.never(),
});

const countCriterion = schema.object({
...baseCriterion,
aggType: schema.literal('count'),
metric: schema.never(),
customMetrics: schema.never(),
equation: schema.never(),
label: schema.never(),
});

const customCriterion = schema.object({
...baseCriterion,
aggType: schema.literal('custom'),
metric: schema.never(),
customMetrics: schema.arrayOf(
schema.oneOf([
schema.object({
name: schema.string(),
aggType: oneOfLiterals(['avg', 'sum', 'max', 'min', 'cardinality']),
field: schema.string(),
filter: schema.never(),
}),
schema.object({
name: schema.string(),
aggType: schema.literal('count'),
filter: schema.maybe(schema.string()),
field: schema.never(),
}),
])
),
equation: schema.maybe(schema.string()),
label: schema.maybe(schema.string()),
});

export const metricThresholdRuleParamsSchema = schema.object(
{
criteria: schema.arrayOf(schema.oneOf([countCriterion, nonCountCriterion, customCriterion])),
groupBy: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
filterQuery: schema.maybe(
schema.string({
validate: validateIsStringElasticsearchJSONFilter,
})
),
sourceId: schema.string(),
alertOnNoData: schema.maybe(schema.boolean()),
alertOnGroupDisappear: schema.maybe(schema.boolean()),
},
{ unknowns: 'allow' }
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@
},
"include": ["**/*.ts"],
"exclude": ["target/**/*"],
"kbn_references": ["@kbn/config-schema", "@kbn/ml-anomaly-utils"]
"kbn_references": [
"@kbn/config-schema",
"@kbn/ml-anomaly-utils",
"@kbn/alerting-comparators",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
* 2.0.
*/

import { isEmpty, isError } from 'lodash';
import { schema } from '@kbn/config-schema';
import { isError } from 'lodash';
import type { Logger, LogMeta } from '@kbn/logging';
import type { ElasticsearchClient } from '@kbn/core/server';
import type { ObservabilityConfig } from '@kbn/observability-plugin/server';
Expand Down Expand Up @@ -52,30 +51,6 @@ const SUPPORTED_ES_FIELD_TYPES = [
ES_FIELD_TYPES.BOOLEAN,
];

export const oneOfLiterals = (arrayOfLiterals: Readonly<string[]>) =>
schema.string({
validate: (value) =>
arrayOfLiterals.includes(value) ? undefined : `must be one of ${arrayOfLiterals.join(' | ')}`,
});

export const validateIsStringElasticsearchJSONFilter = (value: string) => {
if (value === '') {
// Allow clearing the filter.
return;
}

const errorMessage = 'filterQuery must be a valid Elasticsearch filter expressed in JSON';
try {
const parsedValue = JSON.parse(value);
if (!isEmpty(parsedValue.bool)) {
return undefined;
}
return errorMessage;
} catch (e) {
return errorMessage;
}
};

export const UNGROUPED_FACTORY_KEY = '*';

export const createScopedLogger = (
Expand Down
Loading

0 comments on commit 4873fa1

Please sign in to comment.