Skip to content

Commit

Permalink
feat(ClusterInfo): add slo logs link (#1850)
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmufazalov authored Jan 21, 2025
1 parent e99b217 commit 7e0429b
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 124 deletions.
3 changes: 2 additions & 1 deletion src/containers/Cluster/ClusterInfo/ClusterInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import type {VersionToColorMap} from '../../../types/versions';
import i18n from '../i18n';

import {b} from './shared';
import {getInfo, useClusterLinks} from './utils';
import {useClusterLinks} from './utils/useClusterLinks';
import {getInfo} from './utils/utils';

import './ClusterInfo.scss';

Expand Down
123 changes: 0 additions & 123 deletions src/containers/Cluster/ClusterInfo/utils.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {prepareClusterCoresLink, prepareClusterLoggingLinks} from '../useClusterLinks';

describe('prepareClusterCoresLink', () => {
it('It should parse stringified json with cores url', () => {
expect(
prepareClusterCoresLink('{"url":"https://coredumps.com?cluster=my_cluster"}'),
).toEqual('https://coredumps.com?cluster=my_cluster');
});

it('It should return undefined if input is undefined', () => {
expect(prepareClusterCoresLink(undefined)).toEqual(undefined);
});
it('It should return undefined if input is incorrect', () => {
expect(prepareClusterCoresLink('hello')).toEqual(undefined);
});
});

describe('prepareClusterLoggingLinks', () => {
it('It should parse stringified json with logging and slo logs urls', () => {
expect(
prepareClusterLoggingLinks(
'{"url":"https://logging.com/logs?cluster=my_cluster","slo_logs_url":"https://logging.com/slo-logs?cluster=my_cluster"}',
),
).toEqual({
logsUrl: 'https://logging.com/logs?cluster=my_cluster',
sloLogsUrl: 'https://logging.com/slo-logs?cluster=my_cluster',
});
});
it('It should parse stringified json with only logging url', () => {
expect(
prepareClusterLoggingLinks('{"url":"https://logging.com/logs?cluster=my_cluster"}'),
).toEqual({
logsUrl: 'https://logging.com/logs?cluster=my_cluster',
});
});
it('It should parse stringified json with only slo logs url', () => {
expect(
prepareClusterLoggingLinks(
'{"slo_logs_url":"https://logging.com/slo-logs?cluster=my_cluster"}',
),
).toEqual({
sloLogsUrl: 'https://logging.com/slo-logs?cluster=my_cluster',
});
});
it('It should return empty object if input is undefined', () => {
expect(prepareClusterLoggingLinks(undefined)).toEqual({});
});
it('It should return empty object if input is incorrect', () => {
expect(prepareClusterLoggingLinks('hello')).toEqual({});
});
});
83 changes: 83 additions & 0 deletions src/containers/Cluster/ClusterInfo/utils/useClusterLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';

import {useClusterBaseInfo} from '../../../../store/reducers/cluster/cluster';
import type {ClusterLink} from '../../../../types/additionalProps';
import {parseJson} from '../../../../utils/utils';
import i18n from '../../i18n';

/**
* parses stringified json in format {url: "href"}
*/
export function prepareClusterCoresLink(rawCoresString?: string) {
try {
const linkObject = parseJson(rawCoresString) as unknown;

if (
linkObject &&
typeof linkObject === 'object' &&
'url' in linkObject &&
typeof linkObject.url === 'string'
) {
return linkObject.url;
}
} catch {}

return undefined;
}

/**
* parses stringified json in format {url: "href", slo_logs_url: "href"}
*/
export function prepareClusterLoggingLinks(rawLoggingString?: string) {
try {
const linkObject = parseJson(rawLoggingString) as unknown;

if (linkObject && typeof linkObject === 'object') {
const logsUrl =
'url' in linkObject && typeof linkObject.url === 'string'
? linkObject.url
: undefined;
const sloLogsUrl =
'slo_logs_url' in linkObject && typeof linkObject.slo_logs_url === 'string'
? linkObject.slo_logs_url
: undefined;
return {logsUrl, sloLogsUrl};
}
} catch {}

return {};
}

export function useClusterLinks() {
const {cores, logging} = useClusterBaseInfo();

return React.useMemo(() => {
const result: ClusterLink[] = [];

const coresUrl = prepareClusterCoresLink(cores);
const {logsUrl, sloLogsUrl} = prepareClusterLoggingLinks(logging);

if (coresUrl) {
result.push({
title: i18n('link_cores'),
url: coresUrl,
});
}

if (logsUrl) {
result.push({
title: i18n('link_logging'),
url: logsUrl,
});
}

if (sloLogsUrl) {
result.push({
title: i18n('link_slo-logs'),
url: sloLogsUrl,
});
}

return result;
}, [cores, logging]);
}
72 changes: 72 additions & 0 deletions src/containers/Cluster/ClusterInfo/utils/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';

import {Flex} from '@gravity-ui/uikit';

import {ProgressViewer} from '../../../../components/ProgressViewer/ProgressViewer';
import {Tags} from '../../../../components/Tags';
import {isClusterInfoV2} from '../../../../types/api/cluster';
import type {TClusterInfo} from '../../../../types/api/cluster';
import type {EFlag} from '../../../../types/api/enums';
import type {InfoItem} from '../../../../types/components';
import {formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
import i18n from '../../i18n';
import {NodesState} from '../components/NodesState/NodesState';
import {b} from '../shared';

const COLORS_PRIORITY: Record<EFlag, number> = {
Green: 5,
Blue: 4,
Yellow: 3,
Orange: 2,
Red: 1,
Grey: 0,
};

const getDCInfo = (cluster: TClusterInfo) => {
if (isClusterInfoV2(cluster) && cluster.MapDataCenters) {
return Object.entries(cluster.MapDataCenters).map(([dc, count]) => (
<React.Fragment key={dc}>
{dc}: {formatNumber(count)}
</React.Fragment>
));
}
return undefined;
};

export const getInfo = (cluster: TClusterInfo, additionalInfo: InfoItem[]) => {
const info: InfoItem[] = [];

if (isClusterInfoV2(cluster) && cluster.MapNodeStates) {
const arrayNodesStates = Object.entries(cluster.MapNodeStates) as [EFlag, number][];
// sort stack to achieve order "green, orange, yellow, red, blue, grey"
arrayNodesStates.sort((a, b) => COLORS_PRIORITY[b[0]] - COLORS_PRIORITY[a[0]]);
const nodesStates = arrayNodesStates.map(([state, count]) => {
return (
<NodesState state={state as EFlag} key={state}>
{formatNumber(count)}
</NodesState>
);
});
info.push({
label: i18n('label_nodes-state'),
value: <Flex gap={2}>{nodesStates}</Flex>,
});
}

const dataCenters = getDCInfo(cluster);
if (dataCenters?.length) {
info.push({
label: i18n('label_dc'),
value: <Tags tags={dataCenters} gap={2} className={b('dc')} />,
});
}

info.push({
label: i18n('label_load'),
value: <ProgressViewer value={cluster?.LoadAverage} capacity={cluster?.NumberOfCpus} />,
});

info.push(...additionalInfo);

return info;
};
1 change: 1 addition & 0 deletions src/containers/Cluster/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"links": "Links",
"link_cores": "Coredumps",
"link_logging": "Logging",
"link_slo-logs": "Slo Logs",
"context_cores": "cores",
"title_cpu": "CPU",
"title_storage": "Storage",
Expand Down

0 comments on commit 7e0429b

Please sign in to comment.