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:
Bena Kansara 2022-10-13 01:41:15 +02:00 committed by GitHub
parent 702827b42f
commit 4f3558a89c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 269 additions and 5 deletions

View file

@ -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.',
}
);

View file

@ -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

View file

@ -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);
}

View file

@ -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 },
},
},
},

View file

@ -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) {

View file

@ -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 },
],
},
});

View file

@ -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',

View file

@ -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,
},
},
});
});