Skip to content

Commit

Permalink
feat(slo): group by instance id (elastic#186131)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdelemme authored and seanrathier committed Jun 21, 2024
1 parent 989f80e commit 9def784
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const groupBySchema = t.union([
t.literal('slo.tags'),
t.literal('status'),
t.literal('slo.indicator.type'),
t.literal('slo.instanceId'),
t.literal('_index'),
]);

Expand Down
1 change: 1 addition & 0 deletions x-pack/packages/kbn-slo-schema/src/schema/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const groupSummarySchema = t.type({
id: t.string,
instanceId: t.string,
name: t.string,
groupings: t.record(t.string, t.unknown),
}),
}),
violated: t.number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ import React, { memo, useState } from 'react';
import { paths } from '../../../../../common/locators/paths';
import { useFetchSloList } from '../../../../hooks/use_fetch_slo_list';
import { useKibana } from '../../../../utils/kibana_react';
import { SLI_OPTIONS } from '../../../slo_edit/constants';
import { useSloFormattedSLIValue } from '../../hooks/use_slo_summary';
import type { SortDirection, SortField } from '../../hooks/use_url_search_state';
import { SlosView } from '../slos_view';
import { GroupByField } from '../slo_list_group_by';
import { SLOView } from '../toggle_slo_view';
import { useGroupName } from './hooks/use_group_name';

interface Props {
group: string;
Expand All @@ -57,26 +57,7 @@ export function GroupListView({
}: Props) {
const groupQuery = `"${groupBy}": "${group}"`;
const query = kqlQuery ? `${groupQuery} and ${kqlQuery}` : groupQuery;
let groupName = group.toLowerCase();
if (groupBy === 'slo.indicator.type') {
groupName = SLI_OPTIONS.find((option) => option.value === group)?.text ?? group;
}
if (groupBy === '_index') {
// get remote cluster name from index name
if (groupName.includes(':.')) {
const [remoteClusterName] = groupName.split(':.');
groupName = i18n.translate('xpack.slo.group.remoteCluster', {
defaultMessage: 'Remote Cluster: {remoteClusterName}',
values: {
remoteClusterName,
},
});
} else {
groupName = i18n.translate('xpack.slo.group.remoteCluster.localKibana', {
defaultMessage: 'Local Kibana',
});
}
}
const groupName = useGroupName(groupBy, group, summary);

const [page, setPage] = useState(0);
const [accordionState, setAccordionState] = useState<'open' | 'closed'>('closed');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,65 @@ describe('Group View', () => {
{
group: 'production',
groupBy: 'slo.tags',
summary: { total: 3, worst: 0.95, healthy: 2, violated: 1, degrading: 0, noData: 0 },
summary: {
total: 3,
worst: {
sliValue: 1,
status: 'healthy',
slo: {
id: 'irrelevant',
instanceId: 'irrelevant',
name: 'irrelevant',
groupings: {},
},
},
healthy: 2,
violated: 1,
degrading: 0,
noData: 0,
},
},
{
group: 'something',
groupBy: 'slo.tags',
summary: { total: 1, worst: 0.9, healthy: 0, violated: 1, degrading: 0, noData: 0 },
summary: {
total: 1,
worst: {
sliValue: 1,
status: 'healthy',
slo: {
id: 'irrelevant',
instanceId: 'irrelevant',
name: 'irrelevant',
groupings: {},
},
},
healthy: 0,
violated: 1,
degrading: 0,
noData: 0,
},
},
{
group: 'anything',
groupBy: 'slo.tags',
summary: { total: 2, worst: 0.85, healthy: 1, violated: 0, degrading: 0, noData: 1 },
summary: {
total: 2,
worst: {
sliValue: 1,
status: 'healthy',
slo: {
id: 'irrelevant',
instanceId: 'irrelevant',
name: 'irrelevant',
groupings: {},
},
},
healthy: 1,
violated: 0,
degrading: 0,
noData: 1,
},
},
],
},
Expand Down Expand Up @@ -163,7 +211,7 @@ describe('Group View', () => {
results: [
{
group: 'production',
groupBy: 'tags',
groupBy: 'slo.tags',
summary: { total: 3, worst: 0.95, healthy: 2, violated: 1, degrading: 0, noData: 0 },
},
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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 { SLO_SUMMARY_DESTINATION_INDEX_PATTERN } from '../../../../../../common/constants';
import { GroupSummary } from '@kbn/slo-schema';
import { useGroupName } from './use_group_name';

describe('useGroupName', () => {
it('returns the group name for ungrouped', () => {
const groupName = useGroupName('ungrouped', 'irrelevant', {} as GroupSummary);
expect(groupName).toBe('irrelevant');
});

it('returns the group name for slo tags', () => {
const groupName = useGroupName('slo.tags', 'some-tag', {} as GroupSummary);
expect(groupName).toBe('some-tag');
});

it('returns the group name for status', () => {
const groupName = useGroupName('status', 'HEALTHY', {} as GroupSummary);
expect(groupName).toBe('healthy');
});

it('returns the group name for slo instanceId', () => {
const summary = {
total: 2,
violated: 0,
healthy: 2,
degrading: 0,
noData: 0,
worst: {
sliValue: 1,
status: 'healthy',
slo: {
id: 'slo-id',
name: 'slo-name',
instanceId: 'domain.com',
groupings: {
host: {
name: 'domain.com',
},
},
},
},
} as GroupSummary;
const groupName = useGroupName('slo.instanceId', 'domain.com', summary);
expect(groupName).toBe('host.name: domain.com');
});

it('returns the group name for slo indicator type', () => {
const groupName = useGroupName('slo.indicator.type', 'sli.kql.custom', {} as GroupSummary);
expect(groupName).toBe('Custom Query');
});

it('returns the group name for local index', () => {
const groupName = useGroupName(
'_index',
SLO_SUMMARY_DESTINATION_INDEX_PATTERN,
{} as GroupSummary
);
expect(groupName).toBe('Local Kibana');
});

it('returns the group name for remote index', () => {
const groupName = useGroupName(
'_index',
`my-remote-cluster:${SLO_SUMMARY_DESTINATION_INDEX_PATTERN}`,
{} as GroupSummary
);
expect(groupName).toBe('Remote Cluster: my-remote-cluster');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { ALL_VALUE, GroupSummary } from '@kbn/slo-schema';
import { assertNever } from '@kbn/std';
import { SLI_OPTIONS } from '../../../../slo_edit/constants';
import { GroupByField } from '../../slo_list_group_by';

export function useGroupName(groupBy: GroupByField, group: string, summary?: GroupSummary) {
const groupName = group.toLowerCase();

switch (groupBy) {
case 'ungrouped':
case 'slo.tags':
case 'status':
return groupName;
case 'slo.instanceId':
if (groupName === ALL_VALUE || !summary) {
return i18n.translate('xpack.slo.group.ungroupedInstanceId', {
defaultMessage: 'Ungrouped',
});
}

const groupNames = flattenObject(summary.worst.slo.groupings);
return Object.entries(groupNames)
.map(([key, value]) => `${key}: ${value}`)
.join(', ');
case 'slo.indicator.type':
return SLI_OPTIONS.find((option) => option.value === group)?.text ?? groupName;
case '_index':
if (groupName.includes(':.')) {
const [remoteClusterName] = groupName.split(':.');
return i18n.translate('xpack.slo.group.remoteCluster', {
defaultMessage: 'Remote Cluster: {remoteClusterName}',
values: {
remoteClusterName,
},
});
}

return i18n.translate('xpack.slo.group.remoteCluster.localKibana', {
defaultMessage: 'Local Kibana',
});

default:
assertNever(groupBy);
}
}

function flattenObject(obj: Record<string, any>, parentKey = '', result: Record<string, any> = {}) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
flattenObject(obj[key], newKey, result);
} else {
result[newKey] = obj[key];
}
}
}
return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import type { SearchState } from '../hooks/use_url_search_state';
import type { Option } from './slo_context_menu';
import { ContextMenuItem, SLOContextMenu } from './slo_context_menu';

export type GroupByField = 'ungrouped' | 'slo.tags' | 'status' | 'slo.indicator.type' | '_index';
export type GroupByField =
| 'ungrouped'
| 'slo.tags'
| 'status'
| 'slo.indicator.type'
| 'slo.instanceId'
| '_index';
export interface Props {
onStateChange: (newState: Partial<SearchState>) => void;
state: SearchState;
Expand Down Expand Up @@ -80,6 +86,16 @@ export function SloGroupBy({ onStateChange, state, loading }: Props) {
handleChangeGroupBy('slo.indicator.type');
},
},
{
label: i18n.translate('xpack.slo.list.groupBy.sloInstanceId', {
defaultMessage: 'SLO instance id',
}),
checked: groupBy === 'slo.instanceId',
value: 'slo.instanceId',
onClick: () => {
handleChangeGroupBy('slo.instanceId');
},
},
];

if (hasRemoteEnabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,14 @@ export class FindSLOGroups {
},
},
_source: {
includes: ['sliValue', 'status', 'slo.id', 'slo.instanceId', 'slo.name'],
includes: [
'sliValue',
'status',
'slo.id',
'slo.instanceId',
'slo.name',
'slo.groupings',
],
},
size: 1,
},
Expand Down Expand Up @@ -165,6 +172,7 @@ export class FindSLOGroups {
id: sourceSummaryDoc.slo.id,
instanceId: sourceSummaryDoc.slo.instanceId,
name: sourceSummaryDoc.slo.name,
groupings: sourceSummaryDoc.slo.groupings,
},
},
violated: bucket.violated?.doc_count,
Expand Down

0 comments on commit 9def784

Please sign in to comment.