Skip to content

Commit

Permalink
[8.x] [ResponseOps] Move the params of SLO, observability, and logs r…
Browse files Browse the repository at this point in the history
…ule types rule-params package (#205507) (#208536)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[ResponseOps] Move the params of SLO, observability, and logs rule
types rule-params package
(#205507)](#205507)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT
[{"author":{"name":"Julia","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-01-28T13:56:22Z","message":"[ResponseOps]
Move the params of SLO, observability, and logs rule types rule-params
package (#205507)\n\nPartly resolve:
#195191 (cover\r\nlog threshold
and slo burn rate)\r\n\r\nFor log threshold rule we decided to just copy
paste rule parameters and\r\nuse it during registering rule, because
existing schema is written using\r\n'io-ts' library, but all our schemas
should use '@kbn/config-schema' in\r\nout rule-params package. We did it
as a temp solution. I'll create a\r\nfollow up ticket to use our schema
and inherited types everywhere in the\r\ncode.\r\n\r\nFor the custom
threshold rule parameters will be added in separate PR\r\nafter I
merging dataViewSpecSchema.\r\n\r\n### Checklist\r\n\r\n- [
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or
tutorials\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>\r\nCo-authored-by:
Christos Nasikas
<[email protected]>","sha":"9b17623984dd6a18317ca0909ff00c3464eba383","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:ResponseOps","v9.0.0","backport:prev-minor","Team:obs-ux-infra_services","Team:obs-ux-management","v8.18.0"],"title":"[ResponseOps]
Move the params of SLO, observability, and logs rule types rule-params
package","number":205507,"url":"https://github.com/elastic/kibana/pull/205507","mergeCommit":{"message":"[ResponseOps]
Move the params of SLO, observability, and logs rule types rule-params
package (#205507)\n\nPartly resolve:
#195191 (cover\r\nlog threshold
and slo burn rate)\r\n\r\nFor log threshold rule we decided to just copy
paste rule parameters and\r\nuse it during registering rule, because
existing schema is written using\r\n'io-ts' library, but all our schemas
should use '@kbn/config-schema' in\r\nout rule-params package. We did it
as a temp solution. I'll create a\r\nfollow up ticket to use our schema
and inherited types everywhere in the\r\ncode.\r\n\r\nFor the custom
threshold rule parameters will be added in separate PR\r\nafter I
merging dataViewSpecSchema.\r\n\r\n### Checklist\r\n\r\n- [
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or
tutorials\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>\r\nCo-authored-by:
Christos Nasikas
<[email protected]>","sha":"9b17623984dd6a18317ca0909ff00c3464eba383"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/205507","number":205507,"mergeCommit":{"message":"[ResponseOps]
Move the params of SLO, observability, and logs rule types rule-params
package (#205507)\n\nPartly resolve:
#195191 (cover\r\nlog threshold
and slo burn rate)\r\n\r\nFor log threshold rule we decided to just copy
paste rule parameters and\r\nuse it during registering rule, because
existing schema is written using\r\n'io-ts' library, but all our schemas
should use '@kbn/config-schema' in\r\nout rule-params package. We did it
as a temp solution. I'll create a\r\nfollow up ticket to use our schema
and inherited types everywhere in the\r\ncode.\r\n\r\nFor the custom
threshold rule parameters will be added in separate PR\r\nafter I
merging dataViewSpecSchema.\r\n\r\n### Checklist\r\n\r\n- [
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or
tutorials\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>\r\nCo-authored-by:
Christos Nasikas
<[email protected]>","sha":"9b17623984dd6a18317ca0909ff00c3464eba383"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Julia <[email protected]>
  • Loading branch information
kibanamachine and guskovaue authored Jan 28, 2025
1 parent 01e51d3 commit 5340aed
Show file tree
Hide file tree
Showing 15 changed files with 268 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { validateKQLStringFilter } from './utils';

describe('validateKQLStringFilter', () => {
const data = [
// input, output
['', undefined],
['host.name:host-0', undefined],
];
const dataWithError = [
// input, output
[
':*',
'filterQuery must be a valid KQL filter (error: Expected "(", NOT, end of input, field name, value, whitespace but ":" found.',
],
];

test.each(data)('validateKQLStringFilter(%s): %o', (input: any, output: any) => {
expect(validateKQLStringFilter(input)).toEqual(output);
});

test.each(dataWithError)('validateKQLStringFilter(%s): %o', (input: any, output: any) => {
expect(validateKQLStringFilter(input)).toContain(output);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { schema } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
import { isEmpty } from 'lodash';
import { buildEsQuery as kbnBuildEsQuery } from '@kbn/es-query';

export const jobsSelectionSchema = schema.object(
{
Expand Down Expand Up @@ -51,6 +52,29 @@ export const validateIsStringElasticsearchJSONFilter = (value: string) => {
}
};

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

try {
kbnBuildEsQuery(undefined, [{ query: value, language: 'kuery' }], [], {
allowLeadingWildcards: true,
queryStringOptions: {},
ignoreFilterIfFieldNotInIndex: false,
});
} catch (e) {
return i18n.translate(
'xpack.responseOps.ruleParams.customThreshold.schema.invalidFilterQuery',
{
defaultMessage: 'filterQuery must be a valid KQL filter (error: {errorMessage})',
values: { errorMessage: e?.message },
}
);
}
};

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

export enum LEGACY_COMPARATORS {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 { logThresholdParamsSchema } from './latest';
export { logThresholdParamsSchema as logThresholdParamsSchemaV1 } from './v1';

export type { LogThresholdParams } from './latest';
export type { LogThresholdParams as LogThresholdParamsV1 } 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,103 @@
/*
* 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';

const persistedLogViewReferenceSchema = schema.object({
logViewId: schema.string(),
type: schema.literal('log-view-reference'),
});

// Comparators //
enum Comparator {
GT = 'more than',
GT_OR_EQ = 'more than or equals',
LT = 'less than',
LT_OR_EQ = 'less than or equals',
EQ = 'equals',
NOT_EQ = 'does not equal',
MATCH = 'matches',
NOT_MATCH = 'does not match',
MATCH_PHRASE = 'matches phrase',
NOT_MATCH_PHRASE = 'does not match phrase',
}

const ComparatorSchema = schema.oneOf([
schema.literal(Comparator.GT),
schema.literal(Comparator.GT_OR_EQ),
schema.literal(Comparator.LT),
schema.literal(Comparator.LT_OR_EQ),
schema.literal(Comparator.EQ),
schema.literal(Comparator.NOT_EQ),
schema.literal(Comparator.MATCH),
schema.literal(Comparator.NOT_MATCH),
schema.literal(Comparator.MATCH_PHRASE),
schema.literal(Comparator.NOT_MATCH_PHRASE),
]);

const ThresholdSchema = schema.object({
comparator: ComparatorSchema,
value: schema.number(),
});

const criterionSchema = schema.object({
field: schema.string(),
comparator: ComparatorSchema,
value: schema.oneOf([schema.string(), schema.number()]),
});

const countCriteriaSchema = schema.arrayOf(criterionSchema);
const ratioCriteriaSchema = schema.arrayOf(countCriteriaSchema);

const timeUnitSchema = schema.oneOf([
schema.literal('s'),
schema.literal('m'),
schema.literal('h'),
schema.literal('d'),
]);

const timeSizeSchema = schema.number();
const groupBySchema = schema.arrayOf(schema.string());

const RequiredRuleParamsSchema = schema.object({
// NOTE: "count" would be better named as "threshold", but this would require a
// migration of encrypted saved objects, so we'll keep "count" until it's problematic.
count: ThresholdSchema,
timeUnit: timeUnitSchema,
timeSize: timeSizeSchema,
logView: persistedLogViewReferenceSchema, // Alerts are only compatible with persisted Log Views
});

const OptionalRuleParamsSchema = schema.object({
groupBy: schema.maybe(groupBySchema),
});

const countRuleParamsSchema = schema.intersection([
schema.object({
criteria: countCriteriaSchema,
}),
RequiredRuleParamsSchema,
OptionalRuleParamsSchema,
]);

const ratioRuleParamsSchema = schema.intersection([
schema.object({
criteria: ratioCriteriaSchema,
}),
RequiredRuleParamsSchema,
OptionalRuleParamsSchema,
]);

export const logThresholdParamsSchema = schema.oneOf([
countRuleParamsSchema,
ratioRuleParamsSchema,
]);

// Export types for TypeScript
export type LogThresholdParams = ReturnType<typeof logThresholdParamsSchema.validate>;
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 { sloBurnRateParamsSchema } from './latest';
export { sloBurnRateParamsSchema as sloBurnRateParamsSchemaV1 } 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,35 @@
/*
* 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';

const durationSchema = schema.object({
value: schema.number(),
unit: schema.string(),
});

const windowSchema = schema.object({
id: schema.string(),
burnRateThreshold: schema.number(),
maxBurnRateThreshold: schema.nullable(schema.number()),
longWindow: durationSchema,
shortWindow: durationSchema,
actionGroup: schema.string(),
});

const dependency = schema.object({
ruleId: schema.string(),
actionGroupsToSuppressOn: schema.arrayOf(schema.string()),
});

export const sloBurnRateParamsSchema = schema.object({
sloId: schema.string(),
windows: schema.arrayOf(windowSchema),
dependencies: schema.maybe(schema.arrayOf(dependency)),
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"@kbn/ml-anomaly-utils",
"@kbn/i18n",
"@kbn/alerting-comparators",
"@kbn/es-query",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -33723,7 +33723,7 @@
"xpack.observability.customThreshold.rule.reason.forTheLast": "durée : {duration}",
"xpack.observability.customThreshold.rule.reason.group": "groupe : {group}",
"xpack.observability.customThreshold.rule.reasonActionVariableDescription": "Une description concise de la raison du signalement",
"xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery doit être un filtre KQL valide (erreur : {errorMessage})",
"xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery doit être un filtre KQL valide (erreur : {errorMessage})",
"xpack.observability.customThreshold.rule.sourceConfiguration.missingHttp": "Échec de chargement de la source : Aucun client HTTP disponible.",
"xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureBody": "Nous n'avons pas pu appliquer les modifications à la configuration des indicateurs. Réessayez plus tard.",
"xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureTitle": "La mise à jour de la configuration a échoué",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
*/

import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';

import type { RuleParams, CountCriteria, Criterion, ExecutionTimeRange } from '.';
import { type LogThresholdParams } from '@kbn/response-ops-rule-params/log_threshold';
import type { CountCriteria, Criterion, ExecutionTimeRange } from '.';
import { Comparator } from '.';

import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';

export type LogThresholdRuleTypeParams = RuleParams;
export type LogThresholdRuleTypeParams = LogThresholdParams;

export const buildFiltersFromCriteria = (
params: Pick<RuleParams, 'timeSize' | 'timeUnit'> & { criteria: CountCriteria },
params: Pick<LogThresholdParams, 'timeSize' | 'timeUnit'> & { criteria: CountCriteria },
timestampField: string,
executionTimeRange?: ExecutionTimeRange
) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@
import type { SavedObjectReference } from '@kbn/core/server';
import { logViewReferenceRT } from '@kbn/logs-shared-plugin/common';
import { logViewSavedObjectName } from '@kbn/logs-shared-plugin/server';
import { decodeOrThrow } from '@kbn/io-ts-utils';
import type { RuleParams } from '../../../../common/alerting/logs/log_threshold';
import { ruleParamsRT } from '../../../../common/alerting/logs/log_threshold';
import {
type LogThresholdParams,
logThresholdParamsSchema,
} from '@kbn/response-ops-rule-params/log_threshold';

export const LOG_VIEW_REFERENCE_NAME = 'log-view-reference-0';

export const extractReferences = (params: RuleParams) => {
interface ExtractReferencesReturnType {
params: LogThresholdParams;
references: SavedObjectReference[];
}

export const extractReferences = (params: LogThresholdParams): ExtractReferencesReturnType => {
if (!logViewReferenceRT.is(params.logView)) {
return { params, references: [] };
}
Expand All @@ -38,8 +44,11 @@ export const extractReferences = (params: RuleParams) => {
return { params: newParams, references };
};

export const injectReferences = (params: RuleParams, references: SavedObjectReference[]) => {
const decodedParams = decodeOrThrow(ruleParamsRT)(params);
export const injectReferences = (
params: LogThresholdParams,
references: SavedObjectReference[]
): LogThresholdParams => {
const decodedParams = logThresholdParamsSchema.validate(params);

if (!logViewReferenceRT.is(decodedParams.logView)) {
return decodedParams;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ import type {
AlertingServerSetup,
} from '@kbn/alerting-plugin/server';
import { observabilityPaths } from '@kbn/observability-plugin/common';
import { decodeOrThrow } from '@kbn/io-ts-utils';
import { logThresholdParamsSchema } from '@kbn/response-ops-rule-params/log_threshold';
import type { InfraConfig } from '../../../../common/plugin_config_types';
import { O11Y_AAD_FIELDS } from '../../../../common/constants';
import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor';
import { extractReferences, injectReferences } from './log_threshold_references_manager';
import {
LOG_DOCUMENT_COUNT_RULE_TYPE_ID,
ruleParamsRT,
} from '../../../../common/alerting/logs/log_threshold';
import { LOG_DOCUMENT_COUNT_RULE_TYPE_ID } from '../../../../common/alerting/logs/log_threshold';
import type { InfraBackendLibs } from '../../infra_types';
import {
alertDetailUrlActionVariableDescription,
Expand Down Expand Up @@ -126,9 +123,7 @@ export function registerLogThresholdRuleType(
defaultMessage: 'Log threshold',
}),
validate: {
params: {
validate: (params) => decodeOrThrow(ruleParamsRT)(params),
},
params: logThresholdParamsSchema,
},
defaultActionGroupId: FIRED_ACTIONS.id,
actionGroups: [FIRED_ACTIONS],
Expand Down
Loading

0 comments on commit 5340aed

Please sign in to comment.