mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Adds new contextual attributes to Infrastructure - Inventory Rule (#140598)
* adding context for nodetype Hosts for Inventory rule * removing unused import * adding more action variables, fixing failing tests * minor changes * refactoring * removing extra fields being added with context * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * fixing failing tests * refactoring * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * adding component template ref * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * changed description Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
702827b42f
commit
4f3558a89c
8 changed files with 269 additions and 5 deletions
|
@ -214,3 +214,45 @@ export const viewInAppUrlActionVariableDescription = i18n.translate(
|
|||
'Link to the view or feature within Elastic that can be used to investigate the alert and its context further',
|
||||
}
|
||||
);
|
||||
|
||||
export const cloudActionVariableDescription = i18n.translate(
|
||||
'xpack.infra.metrics.alerting.cloudActionVariableDescription',
|
||||
{
|
||||
defaultMessage: 'The cloud object defined by ECS if available in the source.',
|
||||
}
|
||||
);
|
||||
|
||||
export const hostActionVariableDescription = i18n.translate(
|
||||
'xpack.infra.metrics.alerting.hostActionVariableDescription',
|
||||
{
|
||||
defaultMessage: 'The host object defined by ECS if available in the source.',
|
||||
}
|
||||
);
|
||||
|
||||
export const containerActionVariableDescription = i18n.translate(
|
||||
'xpack.infra.metrics.alerting.containerActionVariableDescription',
|
||||
{
|
||||
defaultMessage: 'The container object defined by ECS if available in the source.',
|
||||
}
|
||||
);
|
||||
|
||||
export const orchestratorActionVariableDescription = i18n.translate(
|
||||
'xpack.infra.metrics.alerting.orchestratorActionVariableDescription',
|
||||
{
|
||||
defaultMessage: 'The orchestrator object defined by ECS if available in the source.',
|
||||
}
|
||||
);
|
||||
|
||||
export const labelsActionVariableDescription = i18n.translate(
|
||||
'xpack.infra.metrics.alerting.labelsActionVariableDescription',
|
||||
{
|
||||
defaultMessage: 'List of labels associated with the entity where this alert triggered.',
|
||||
}
|
||||
);
|
||||
|
||||
export const tagsActionVariableDescription = i18n.translate(
|
||||
'xpack.infra.metrics.alerting.tagsActionVariableDescription',
|
||||
{
|
||||
defaultMessage: 'List of tags associated with the entity where this alert triggered.',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -22,6 +22,7 @@ export type ConditionResult = InventoryMetricConditions & {
|
|||
currentValue: number;
|
||||
isNoData: boolean;
|
||||
isError: boolean;
|
||||
context: [x: string];
|
||||
};
|
||||
|
||||
export const evaluateCondition = async ({
|
||||
|
@ -82,6 +83,14 @@ export const evaluateCondition = async ({
|
|||
isNoData: value === null,
|
||||
isError: value === undefined,
|
||||
currentValue: value.value,
|
||||
context: {
|
||||
cloud: value.cloud,
|
||||
host: value.host,
|
||||
container: value.container,
|
||||
orchestrator: value.orchestrator,
|
||||
labels: value.labels,
|
||||
tags: value.tags,
|
||||
},
|
||||
};
|
||||
}) as unknown; // Typescript doesn't seem to know what `throw` is doing
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ type InventoryMetricThresholdAlert = Alert<
|
|||
type InventoryMetricThresholdAlertFactory = (
|
||||
id: string,
|
||||
reason: string,
|
||||
additionalContext?: [x: string] | null,
|
||||
threshold?: number | undefined,
|
||||
value?: number | undefined
|
||||
) => InventoryMetricThresholdAlert;
|
||||
|
@ -66,12 +67,13 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
|||
if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions');
|
||||
const logger = createScopedLogger(libs.logger, 'inventoryRule', { alertId, executionId });
|
||||
const { alertWithLifecycle, savedObjectsClient, getAlertStartedDate } = services;
|
||||
const alertFactory: InventoryMetricThresholdAlertFactory = (id, reason) =>
|
||||
const alertFactory: InventoryMetricThresholdAlertFactory = (id, reason, additionalContext) =>
|
||||
alertWithLifecycle({
|
||||
id,
|
||||
fields: {
|
||||
[ALERT_REASON]: reason,
|
||||
[ALERT_RULE_PARAMETERS]: params as any, // the type assumes the object is already flattened when writing the same way as when reading https://github.com/elastic/kibana/blob/main/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts#L60
|
||||
...additionalContext,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -184,7 +186,9 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
|||
const actionGroupId =
|
||||
nextState === AlertStates.WARNING ? WARNING_ACTIONS.id : FIRED_ACTIONS.id;
|
||||
|
||||
const alert = alertFactory(group, reason);
|
||||
const additionalContext = results && results.length > 0 ? results[0][group].context : null;
|
||||
|
||||
const alert = alertFactory(group, reason, additionalContext);
|
||||
const indexedStartedDate = getAlertStartedDate(group) ?? startedAt.toISOString();
|
||||
const viewInAppUrl = getViewInAppUrlInventory(
|
||||
criteria,
|
||||
|
@ -193,6 +197,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
|||
libs.basePath
|
||||
);
|
||||
scheduledActionsCount++;
|
||||
|
||||
const context = {
|
||||
group,
|
||||
alertState: stateToAlertMessage[nextState],
|
||||
|
@ -204,6 +209,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
|||
),
|
||||
threshold: mapToConditionsLookup(criteria, (c) => c.threshold),
|
||||
metric: mapToConditionsLookup(criteria, (c) => c.metric),
|
||||
...additionalContext,
|
||||
};
|
||||
alert.scheduleActions(actionGroupId, context);
|
||||
}
|
||||
|
|
|
@ -55,6 +55,18 @@ export const createRequest = (
|
|||
const metricAggregations = createMetricAggregations(timerange, nodeType, metric, customMetric);
|
||||
const bucketSelector = createBucketSelector(metric, condition, customMetric);
|
||||
|
||||
const additionalContextAgg = {
|
||||
additionalContext: {
|
||||
top_hits: {
|
||||
size: 1,
|
||||
_source: {
|
||||
includes: ['host.*', 'labels.*', 'tags', 'cloud.*', 'orchestrator.*', 'container.*'],
|
||||
excludes: ['host.cpu.*', 'host.disk.*', 'host.network.*'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const request: ESSearchRequest = {
|
||||
allow_no_indices: true,
|
||||
ignore_unavailable: true,
|
||||
|
@ -65,7 +77,7 @@ export const createRequest = (
|
|||
aggs: {
|
||||
nodes: {
|
||||
composite,
|
||||
aggs: { ...metricAggregations, ...bucketSelector },
|
||||
aggs: { ...metricAggregations, ...bucketSelector, ...additionalContextAgg },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AggregationsAggregate, SearchResponse } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { ElasticsearchClient } from '@kbn/core/server';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy';
|
||||
import { InventoryMetricConditions } from '../../../../../common/alerting/metrics';
|
||||
import { InfraTimerangeInput, SnapshotCustomMetricInput } from '../../../../../common/http_api';
|
||||
import {
|
||||
|
@ -20,13 +22,28 @@ import { createRequest } from './create_request';
|
|||
interface BucketKey {
|
||||
node: string;
|
||||
}
|
||||
type Response = Record<string, { value: number | null; warn: boolean; trigger: boolean }>;
|
||||
|
||||
interface AdditionalContext {
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
type Response = Record<
|
||||
string,
|
||||
{
|
||||
value: number | null;
|
||||
warn: boolean;
|
||||
trigger: boolean;
|
||||
} & AdditionalContext
|
||||
>;
|
||||
|
||||
type Metric = Record<string, { value: number | null }>;
|
||||
|
||||
interface Bucket {
|
||||
key: BucketKey;
|
||||
doc_count: number;
|
||||
shouldWarn: { value: number };
|
||||
shouldTrigger: { value: number };
|
||||
additionalContext: SearchResponse<EcsFieldsResponse, Record<string, AggregationsAggregate>>;
|
||||
}
|
||||
type NodeBucket = Bucket & Metric;
|
||||
interface ResponseAggregations {
|
||||
|
@ -56,10 +73,15 @@ export const getData = async (
|
|||
const nextAfterKey = nodes.after_key;
|
||||
for (const bucket of nodes.buckets) {
|
||||
const metricId = customMetric && customMetric.field ? customMetric.id : metric;
|
||||
const bucketHits = bucket.additionalContext?.hits?.hits;
|
||||
const additionalContextSource =
|
||||
bucketHits && bucketHits.length > 0 ? bucketHits[0]._source : null;
|
||||
|
||||
previous[bucket.key.node] = {
|
||||
value: bucket?.[metricId]?.value ?? null,
|
||||
warn: bucket?.shouldWarn.value > 0 ?? false,
|
||||
trigger: bucket?.shouldTrigger.value > 0 ?? false,
|
||||
...additionalContextSource,
|
||||
};
|
||||
}
|
||||
if (nextAfterKey) {
|
||||
|
|
|
@ -25,9 +25,15 @@ import {
|
|||
import { InfraBackendLibs } from '../../infra_types';
|
||||
import {
|
||||
alertStateActionVariableDescription,
|
||||
cloudActionVariableDescription,
|
||||
containerActionVariableDescription,
|
||||
groupActionVariableDescription,
|
||||
hostActionVariableDescription,
|
||||
labelsActionVariableDescription,
|
||||
metricActionVariableDescription,
|
||||
orchestratorActionVariableDescription,
|
||||
reasonActionVariableDescription,
|
||||
tagsActionVariableDescription,
|
||||
thresholdActionVariableDescription,
|
||||
timestampActionVariableDescription,
|
||||
valueActionVariableDescription,
|
||||
|
@ -102,6 +108,12 @@ export async function registerMetricInventoryThresholdRuleType(
|
|||
{ name: 'metric', description: metricActionVariableDescription },
|
||||
{ name: 'threshold', description: thresholdActionVariableDescription },
|
||||
{ name: 'viewInAppUrl', description: viewInAppUrlActionVariableDescription },
|
||||
{ name: 'cloud', description: cloudActionVariableDescription },
|
||||
{ name: 'host', description: hostActionVariableDescription },
|
||||
{ name: 'container', description: containerActionVariableDescription },
|
||||
{ name: 'orchestrator', description: orchestratorActionVariableDescription },
|
||||
{ name: 'labels', description: labelsActionVariableDescription },
|
||||
{ name: 'tags', description: tagsActionVariableDescription },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import { mappingFromFieldMap } from '@kbn/rule-registry-plugin/common/mapping_fr
|
|||
import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map';
|
||||
|
||||
import { Dataset, RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server';
|
||||
import { ECS_COMPONENT_TEMPLATE_NAME } from '@kbn/rule-registry-plugin/common/assets';
|
||||
import type { InfraFeatureId } from '../../../common/constants';
|
||||
import { RuleRegistrationContext, RulesServiceStartDeps } from './types';
|
||||
|
||||
|
@ -30,7 +31,7 @@ export const createRuleDataClient = ({
|
|||
feature: ownerFeatureId,
|
||||
registrationContext,
|
||||
dataset: Dataset.alerts,
|
||||
componentTemplateRefs: [],
|
||||
componentTemplateRefs: [ECS_COMPONENT_TEMPLATE_NAME],
|
||||
componentTemplates: [
|
||||
{
|
||||
name: 'mappings',
|
||||
|
|
|
@ -102,6 +102,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 1.109,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-0', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -125,6 +133,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 1.0376666666666665,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-0', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -157,6 +173,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 79351.95,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0', network: {} },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'host-1': {
|
||||
metric: 'rx',
|
||||
|
@ -170,6 +194,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 10,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-1', network: {} },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -198,6 +230,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 79351.95,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0', network: {} },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'host-1': {
|
||||
metric: 'rx',
|
||||
|
@ -211,6 +251,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 10,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-1', network: {} },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -240,6 +288,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 125658.70833333333,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0', network: {} },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'host-1': {
|
||||
metric: 'rx',
|
||||
|
@ -253,6 +309,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 11.666666666666668,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-1', network: {} },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -298,6 +362,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 833.3333333333334,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-0', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'host-1': {
|
||||
metric: 'custom',
|
||||
|
@ -318,6 +390,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 1000,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-1' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-1', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -359,6 +439,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 1133.3333333333333,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-0', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'host-1': {
|
||||
metric: 'custom',
|
||||
|
@ -379,6 +467,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 1133.3333333333333,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-1' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-1', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -411,6 +507,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 0.3,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-0', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'host-1': {
|
||||
metric: 'logRate',
|
||||
|
@ -424,6 +528,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 0.3,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-1' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-1', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -453,6 +565,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 0.3,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-0' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-0', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'host-1': {
|
||||
metric: 'logRate',
|
||||
|
@ -466,6 +586,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 0.3,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: { name: 'host-1' },
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: { eventId: 'event-1', groupId: 'group-0' },
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -499,6 +627,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 43332.833333333336,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: undefined,
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'ed01e3a3-4787-42f6-b73e-ac9e97294e9d': {
|
||||
metric: 'rx',
|
||||
|
@ -512,6 +648,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 42783.833333333336,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: undefined,
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -542,6 +686,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 50197.666666666664,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: undefined,
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
'ed01e3a3-4787-42f6-b73e-ac9e97294e9d': {
|
||||
metric: 'rx',
|
||||
|
@ -555,6 +707,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
isNoData: false,
|
||||
isError: false,
|
||||
currentValue: 50622.066666666666,
|
||||
context: {
|
||||
cloud: undefined,
|
||||
host: undefined,
|
||||
container: undefined,
|
||||
orchestrator: undefined,
|
||||
labels: undefined,
|
||||
tags: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue