From 48808c5b594f878a98c85638c6c4d16d7f3af943 Mon Sep 17 00:00:00 2001 From: Tiit Hansen Date: Sat, 25 May 2024 11:44:55 +0300 Subject: [PATCH] feat: Display deamonset desired and ready pods in daemonsets table --- .../Workloads/tabs/DaemonSets/DaemonSets.tsx | 111 +++++++++--------- .../Workloads/tabs/DaemonSets/Queries.tsx | 34 ++++++ 2 files changed, 91 insertions(+), 54 deletions(-) create mode 100644 src/pages/Workloads/tabs/DaemonSets/Queries.tsx diff --git a/src/pages/Workloads/tabs/DaemonSets/DaemonSets.tsx b/src/pages/Workloads/tabs/DaemonSets/DaemonSets.tsx index ce769eb..486a306 100644 --- a/src/pages/Workloads/tabs/DaemonSets/DaemonSets.tsx +++ b/src/pages/Workloads/tabs/DaemonSets/DaemonSets.tsx @@ -17,6 +17,13 @@ import React, { useEffect, useMemo } from 'react'; import { DataFrameView } from '@grafana/data'; import { InteractiveTable } from '../../../../components/InteractiveTable/InterativeTable'; import { buildExpandedRowScene } from './ExpandedRow'; +import { LinkCell } from 'pages/Workloads/components/LinkCell'; +import { ReplicasCell } from 'pages/Workloads/components/ReplicasCell'; +import { CellProps } from 'react-table'; +import { asyncQueryRunner } from 'pages/Workloads/queryHelpers'; +import { getSeriesValue } from 'pages/Workloads/seriesHelpers'; +import { resolveVariable } from 'pages/Workloads/variableHelpers'; +import { createRowQueries } from './Queries'; const namespaceVariable = new QueryVariable({ name: 'namespace', @@ -80,47 +87,46 @@ interface TableRow { cluster: string; daemonset: string; namespace: string; + replicas: { + total: number; + ready: number; + }; } interface TableVizState extends SceneObjectState { expandedRows?: SceneObject[]; - // asyncRowData?: Map; - // asyncDataPods?: string; + asyncRowData?: Map; + visibleRowIds?: string; } class TableViz extends SceneObjectBase { - /*constructor(state: TableVizState) { + constructor(state: TableVizState) { super({ ...state, asyncRowData: new Map() }); - }*/ + } private setAsyncRowData(data: any) { - // this.setState({ ...this.state, asyncRowData: data }); + this.setState({ ...this.state, asyncRowData: data }); } - private setAsyncDataPods(data: string) { - // this.setState({ ...this.state, asyncDataPods: data }); + private setVisibleRowIds(ids: string) { + this.setState({ ...this.state, visibleRowIds: ids }); } static Component = (props: SceneComponentProps) => { const { data } = sceneGraph.getData(props.model).useState(); const sceneVariables = sceneGraph.getVariables(props.model) const timeRange = sceneGraph.getTimeRange(props.model) - // const { asyncRowData } = props.model.useState(); - // const { asyncDataPods } = props.model.useState(); + const { asyncRowData } = props.model.useState(); + const { visibleRowIds } = props.model.useState(); const columns = useMemo( () => [ - { id: 'daemonset', header: 'DAEMONSET' }, - { id: 'namespace', header: 'NAMESPACE' }, - /*{ id: 'node', header: 'NODE', cell: (props: CellProps) => NodeCell(props.row.values.node) }, + { id: 'daemonset', header: 'DAEMONSET', cell: (props: CellProps) => LinkCell('daemonsets', props.row.values.daemonset) }, { id: 'namespace', header: 'NAMESPACE' }, - { id: 'containers', header: 'CONTAINERS', cell: (props: CellProps) => ContainersCellBuilder(props.cell.row.id, asyncRowData) }, - { id: 'restarts', header: 'RESTARTS', cell: (props: CellProps) => RestartsCellBuilder(props.cell.row.id, asyncRowData) }, - { id: 'memory', header: 'MEMORY (U/R/L)', cell: (props: CellProps) => MemoryCellBuilder(props.cell.row.id, asyncRowData) }, - { id: 'cpu', header: 'CPU (U/R/L)', cell: (props: CellProps) => CPUCellBuilder(props.cell.row.id, asyncRowData) },*/ + { id: 'replicas', header: 'REPLICAS', cell: (props: CellProps) => ReplicasCell(props.row.values.replicas) }, ], - [ /*asyncRowData*/] + [] ); const tableData = useMemo(() => { @@ -130,51 +136,48 @@ class TableViz extends SceneObjectBase { const frame = data.series[0]; const view = new DataFrameView(frame); - return view.toArray(); - }, [data]); + const rows = view.toArray(); + + const serieMatcherPredicate = (row: TableRow) => (value: any) => value.daemonset === row.daemonset; + + for (const row of rows) { - /*const onRowsChanged = (rows: any) => { - const pods = rows.map((row: any) => row.id).join('|'); + const total = getSeriesValue(asyncRowData, 'replicas', serieMatcherPredicate(row)) + const ready = getSeriesValue(asyncRowData, 'replicas_ready', serieMatcherPredicate(row)) + + row.replicas = { + total, + ready + } + } - if (!pods || pods.length === 0 || asyncDataPods === pods) { + return rows; + }, [data, asyncRowData]); + + const onRowsChanged = (rows: any) => { + const ids = rows.map((row: any) => row.id).join('|'); + + if (!ids || ids.length === 0 || visibleRowIds === ids) { return; } - const queryRunner = new SceneQueryRunner({ + const datasource = resolveVariable(sceneVariables, 'datasource') + + asyncQueryRunner({ datasource: { - uid: 'prometheus', + uid: datasource?.toString(), type: 'prometheus', }, - queries: createRowQueries(pods), + + queries: [ + ...createRowQueries(ids, sceneVariables), + ], $timeRange: timeRange.clone(), - $variables: sceneVariables.clone() - }) - - queryRunner.addActivationHandler(() => { - - const sub = queryRunner.subscribeToState((state) => { - - const mappedValues: Map = new Map(); - if (state.data && state.data.state === LoadingState.Done) { - for (const series of state.data.series) { - const refId = series.refId; - const frame = new DataFrameView(series); - const data = frame.toArray(); - mappedValues.set(refId || 'unknown', data); - } - } - - props.model.setAsyncDataPods(pods); - props.model.setAsyncRowData(mappedValues); - }) - - return () => { - sub.unsubscribe(); - }; - }) - - queryRunner.activate(); - };*/ + }).then((data) => { + props.model.setVisibleRowIds(ids); + props.model.setAsyncRowData(data); + }); + }; return ( { data={tableData} renderExpandedRow={(row) => } pageSize={10} - // onRowsChanged={onRowsChanged} + onRowsChanged={onRowsChanged} /> ); }; diff --git a/src/pages/Workloads/tabs/DaemonSets/Queries.tsx b/src/pages/Workloads/tabs/DaemonSets/Queries.tsx new file mode 100644 index 0000000..d7644da --- /dev/null +++ b/src/pages/Workloads/tabs/DaemonSets/Queries.tsx @@ -0,0 +1,34 @@ +import { SceneVariables } from "@grafana/scenes"; +import { resolveVariable } from "pages/Workloads/variableHelpers"; + +export function createRowQueries(daemonSet: string, sceneVariables: SceneVariables) { + + const cluster = resolveVariable(sceneVariables, 'cluster'); + + return [ + { + refId: 'replicas', + expr: ` + max( + kube_daemonset_status_desired_number_scheduled{ + daemonset=~"${daemonSet}", + cluster="${cluster}" + } + ) by (daemonset)`, + instant: true, + format: 'table' + }, + { + refId: 'replicas_ready', + expr: ` + max( + kube_daemonset_status_number_ready{ + daemonset=~"${daemonSet}", + cluster="${cluster}" + } + ) by (daemonset)`, + instant: true, + format: 'table' + }, + ]; +}