prometheus
165 строк · 5.1 Кб
1import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
2import { useFetch } from '../../hooks/useFetch';
3import { LabelsTable } from './LabelsTable';
4import { DroppedTarget, Labels, Target } from '../targets/target';
5
6import { withStatusIndicator } from '../../components/withStatusIndicator';
7import { setQuerySearchFilter, mapObjEntries, getQuerySearchFilter } from '../../utils';
8import { usePathPrefix } from '../../contexts/PathPrefixContext';
9import { API_PATH } from '../../constants/constants';
10import { KVSearch } from '@nexucis/kvsearch';
11import { Container } from 'reactstrap';
12import SearchBar from '../../components/SearchBar';
13
14interface ServiceMap {
15activeTargets: Target[];
16droppedTargets: DroppedTarget[];
17droppedTargetCounts: Record<string, number>;
18}
19
20export interface TargetLabels {
21discoveredLabels: Labels;
22labels: Labels;
23isDropped: boolean;
24}
25
26const activeTargetKVSearch = new KVSearch<Target>({
27shouldSort: true,
28indexedKeys: ['labels', 'discoveredLabels', ['discoveredLabels', /.*/], ['labels', /.*/]],
29});
30
31const droppedTargetKVSearch = new KVSearch<DroppedTarget>({
32shouldSort: true,
33indexedKeys: ['discoveredLabels', ['discoveredLabels', /.*/]],
34});
35
36export const processSummary = (
37activeTargets: Target[],
38droppedTargetCounts: Record<string, number>
39): Record<string, { active: number; total: number }> => {
40const targets: Record<string, { active: number; total: number }> = {};
41
42// Get targets of each type along with the total and active end points
43for (const target of activeTargets) {
44const { scrapePool: name } = target;
45if (!targets[name]) {
46targets[name] = {
47total: 0,
48active: 0,
49};
50}
51targets[name].total++;
52targets[name].active++;
53}
54for (const name in targets) {
55if (!targets[name]) {
56targets[name] = {
57total: droppedTargetCounts[name],
58active: 0,
59};
60} else {
61targets[name].total += droppedTargetCounts[name];
62}
63}
64
65return targets;
66};
67
68export const processTargets = (activeTargets: Target[], droppedTargets: DroppedTarget[]): Record<string, TargetLabels[]> => {
69const labels: Record<string, TargetLabels[]> = {};
70
71for (const target of activeTargets) {
72const name = target.scrapePool;
73if (!labels[name]) {
74labels[name] = [];
75}
76labels[name].push({
77discoveredLabels: target.discoveredLabels,
78labels: target.labels,
79isDropped: false,
80});
81}
82
83for (const target of droppedTargets) {
84const { job: name } = target.discoveredLabels;
85if (!labels[name]) {
86labels[name] = [];
87}
88labels[name].push({
89discoveredLabels: target.discoveredLabels,
90isDropped: true,
91labels: {},
92});
93}
94
95return labels;
96};
97
98export const ServiceDiscoveryContent: FC<ServiceMap> = ({ activeTargets, droppedTargets, droppedTargetCounts }) => {
99const [activeTargetList, setActiveTargetList] = useState(activeTargets);
100const [droppedTargetList, setDroppedTargetList] = useState(droppedTargets);
101const [targetList, setTargetList] = useState(processSummary(activeTargets, droppedTargetCounts));
102const [labelList, setLabelList] = useState(processTargets(activeTargets, droppedTargets));
103
104const handleSearchChange = useCallback(
105(value: string) => {
106setQuerySearchFilter(value);
107if (value !== '') {
108const activeTargetResult = activeTargetKVSearch.filter(value.trim(), activeTargets);
109const droppedTargetResult = droppedTargetKVSearch.filter(value.trim(), droppedTargets);
110setActiveTargetList(activeTargetResult.map((value) => value.original));
111setDroppedTargetList(droppedTargetResult.map((value) => value.original));
112} else {
113setActiveTargetList(activeTargets);
114}
115},
116[activeTargets, droppedTargets]
117);
118
119const defaultValue = useMemo(getQuerySearchFilter, []);
120
121useEffect(() => {
122setTargetList(processSummary(activeTargetList, droppedTargetCounts));
123setLabelList(processTargets(activeTargetList, droppedTargetList));
124}, [activeTargetList, droppedTargetList, droppedTargetCounts]);
125
126return (
127<>
128<h2>Service Discovery</h2>
129<Container>
130<SearchBar defaultValue={defaultValue} handleChange={handleSearchChange} placeholder="Filter by labels" />
131</Container>
132<ul>
133{mapObjEntries(targetList, ([k, v]) => (
134<li key={k}>
135<a href={'#' + k}>
136{k} ({v.active} / {v.total} active targets)
137</a>
138</li>
139))}
140</ul>
141<hr />
142{mapObjEntries(labelList, ([k, v]) => {
143return <LabelsTable value={v} name={k} key={k} />;
144})}
145</>
146);
147};
148ServiceDiscoveryContent.displayName = 'ServiceDiscoveryContent';
149
150const ServicesWithStatusIndicator = withStatusIndicator(ServiceDiscoveryContent);
151
152const ServiceDiscovery: FC = () => {
153const pathPrefix = usePathPrefix();
154const { response, error, isLoading } = useFetch<ServiceMap>(`${pathPrefix}/${API_PATH}/targets`);
155return (
156<ServicesWithStatusIndicator
157{...response.data}
158error={error}
159isLoading={isLoading}
160componentTitle="Service Discovery information"
161/>
162);
163};
164
165export default ServiceDiscovery;
166