github.com/thanos-io/thanos@v0.32.5/pkg/ui/react-app/src/pages/serviceDiscovery/Services.tsx (about)

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