Skip to content

Commit

Permalink
[Metrics UI] Filter out APM nodes from the inventory view (elastic#11…
Browse files Browse the repository at this point in the history
…0300) (elastic#111074)

* [Metrics UI] Filter out APM nodes from the inventory view

* Update jest snapshots

* Add tests for fs for filtering out APM nodes

Co-authored-by: Zacqary Adam Xeper <[email protected]>
  • Loading branch information
kibanamachine and Zacqary authored Sep 3, 2021
1 parent 8955d54 commit 38a1f7f
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 91 deletions.
4 changes: 3 additions & 1 deletion x-pack/plugins/infra/common/http_api/metrics_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ export const MetricsAPISeriesRT = rt.intersection([
]);

export const MetricsAPIResponseRT = rt.type({
series: rt.array(MetricsAPISeriesRT),
series: rt.array(
rt.intersection([MetricsAPISeriesRT, rt.partial({ metricsets: rt.array(rt.string) })])
),
info: MetricsAPIPageInfoRT,
});

Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/infra/server/lib/metrics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ export const query = async (
return {
series: groupings.buckets.map((bucket) => {
const keys = Object.values(bucket.key);
return convertHistogramBucketsToTimeseries(
const metricsetNames = bucket.metricsets.buckets.map((m) => m.key);
const timeseries = convertHistogramBucketsToTimeseries(
keys,
options,
bucket.histogram.buckets,
bucketSize * 1000
);
return { ...timeseries, metricsets: metricsetNames };
}),
info: {
afterKey: returnAfterKey ? afterKey : null,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export const createAggregations = (options: MetricsAPIRequest) => {
},
aggregations: createMetricsAggregations(options),
},
metricsets: {
terms: {
field: 'metricset.name',
},
},
};

if (Array.isArray(options.groupBy) && options.groupBy.length) {
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/infra/server/lib/metrics/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ export const HistogramResponseRT = rt.type({
histogram: rt.type({
buckets: rt.array(HistogramBucketRT),
}),
metricsets: rt.type({
buckets: rt.array(
rt.type({
key: rt.string,
doc_count: rt.number,
})
),
}),
});

const GroupingBucketRT = rt.intersection([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ESSearchClient } from '../../../lib/metrics/types';
import { InfraSource } from '../../../lib/sources';
import { transformRequestToMetricsAPIRequest } from './transform_request_to_metrics_api_request';
import { queryAllData } from './query_all_data';
import { transformMetricsApiResponseToSnapshotResponse } from './trasform_metrics_ui_response';
import { transformMetricsApiResponseToSnapshotResponse } from './transform_metrics_ui_response';
import { copyMissingMetrics } from './copy_missing_metrics';
import { LogQueryFields } from '../../../services/log_queries/get_log_query_fields';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { transformMetricsApiResponseToSnapshotResponse } from './transform_metrics_ui_response';

jest.mock('./apply_metadata_to_last_path', () => ({
applyMetadataToLastPath: (series: any) => [{ label: series.id }],
}));

const now = 1630597319235;

describe('transformMetricsApiResponseToSnapshotResponse', () => {
test('filters out nodes from APM which report no data', () => {
const result = transformMetricsApiResponseToSnapshotResponse(
{
// @ts-ignore
metrics: [{ id: 'cpu' }],
},
{
includeTimeseries: false,
nodeType: 'host',
},
{},
{
info: {
interval: 60,
},
series: [
{
metricsets: ['app'],
id: 'apm-node-with-no-data',
columns: [],
rows: [
{
timestamp: now,
cpu: null,
},
],
},
{
metricsets: ['app'],
id: 'apm-node-with-data',
columns: [],
rows: [
{
timestamp: now,
cpu: 1.0,
},
],
},
{
metricsets: ['cpu'],
id: 'metricbeat-node',
columns: [],
rows: [
{
timestamp: now,
cpu: 1.0,
},
],
},
],
}
);
const nodeNames = result.nodes.map((n) => n.name);
expect(nodeNames).toEqual(expect.arrayContaining(['metricbeat-node', 'apm-node-with-data']));
expect(nodeNames).not.toEqual(expect.arrayContaining(['apm-node']));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { get, max, sum, last, isNumber } from 'lodash';
import { SnapshotMetricType } from '../../../../common/inventory_models/types';
import {
MetricsAPIResponse,
SnapshotNodeResponse,
MetricsAPIRequest,
MetricsExplorerColumnType,
MetricsAPIRow,
SnapshotRequest,
SnapshotNodePath,
SnapshotNodeMetric,
SnapshotNode,
} from '../../../../common/http_api';
import { META_KEY } from './constants';
import { InfraSource } from '../../../lib/sources';
import { applyMetadataToLastPath } from './apply_metadata_to_last_path';

const getMetricValue = (row: MetricsAPIRow) => {
if (!isNumber(row.metric_0)) return null;
const value = row.metric_0;
return isFinite(value) ? value : null;
};

const calculateMax = (rows: MetricsAPIRow[]) => {
return max(rows.map(getMetricValue)) || 0;
};

const calculateAvg = (rows: MetricsAPIRow[]): number => {
return sum(rows.map(getMetricValue)) / rows.length || 0;
};

const getLastValue = (rows: MetricsAPIRow[]) => {
const row = last(rows);
if (!row) return null;
return getMetricValue(row);
};

export const transformMetricsApiResponseToSnapshotResponse = (
options: MetricsAPIRequest,
snapshotRequest: SnapshotRequest,
source: InfraSource,
metricsApiResponse: MetricsAPIResponse
): SnapshotNodeResponse => {
const nodes = metricsApiResponse.series
.map((series) => {
const node = {
metrics: options.metrics
.filter((m) => m.id !== META_KEY)
.map((metric) => {
const name = metric.id as SnapshotMetricType;
const timeseries = {
id: name,
columns: [
{ name: 'timestamp', type: 'date' as MetricsExplorerColumnType },
{ name: 'metric_0', type: 'number' as MetricsExplorerColumnType },
],
rows: series.rows.map((row) => {
return { timestamp: row.timestamp, metric_0: get(row, metric.id, null) };
}),
};
const maxValue = calculateMax(timeseries.rows);
const avg = calculateAvg(timeseries.rows);
const value = getLastValue(timeseries.rows);
const nodeMetric: SnapshotNodeMetric = { name, max: maxValue, value, avg };
if (snapshotRequest.includeTimeseries) {
nodeMetric.timeseries = timeseries;
}
return nodeMetric;
}),
path:
series.keys?.map((key) => {
return { value: key, label: key } as SnapshotNodePath;
}) ?? [],
name: '',
};

const isNoData = node.metrics.every((m) => m.value === null);
const isAPMNode = series.metricsets?.includes('app');
if (isNoData && isAPMNode) return null;

const path = applyMetadataToLastPath(series, node, snapshotRequest, source);
const lastPath = last(path);
const name = lastPath?.label ?? 'N/A';

return { ...node, path, name };
})
.filter((n) => n !== null) as SnapshotNode[];
return { nodes, interval: `${metricsApiResponse.info.interval}s` };
};

This file was deleted.

0 comments on commit 38a1f7f

Please sign in to comment.