Skip to content

Commit

Permalink
feat: Basic queries for Job table
Browse files Browse the repository at this point in the history
  • Loading branch information
tiithansen committed May 25, 2024
1 parent 327f52f commit e2149a8
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 60 deletions.
12 changes: 8 additions & 4 deletions src/pages/Workloads/seriesHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
export function getSeriesValue(asyncData: any, name: string, pred: (value: any) => boolean) {
export function getSeries(asyncData: any, name: string, pred: (value: any) => boolean) {
if (asyncData && asyncData.get(name)) {
const val = asyncData.get(name).find(pred)
return val ? val[`Value #${name}`] : 0
return asyncData.get(name).find(pred)
}
return 0
return null
}

export function getSeriesValue(asyncData: any, name: string, pred: (value: any) => boolean) {
const val = getSeries(asyncData, name, pred)
return val ? val[`Value #${name}`] : 0
}
117 changes: 61 additions & 56 deletions src/pages/Workloads/tabs/Jobs/Jobs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import {
import React, { useEffect, useMemo } from 'react';
import { DataFrameView } from '@grafana/data';
import { InteractiveTable } from '../../../../components/InteractiveTable/InterativeTable';
import { asyncQueryRunner } from 'pages/Workloads/queryHelpers';
import { getSeries } from 'pages/Workloads/seriesHelpers';
import { resolveVariable } from 'pages/Workloads/variableHelpers';
import { createRowQueries } from './Queries';
import { CellProps } from '@grafana/ui';
import { LinkCell } from 'pages/Workloads/components/LinkCell';

const namespaceVariable = new QueryVariable({
name: 'namespace',
Expand Down Expand Up @@ -48,7 +54,7 @@ const jobsQueryRunner = new SceneQueryRunner({
queries: [
{
refId: 'cronjobs',
expr: `group(kube_job_labels{cluster="$cluster", namespace=~"$namespace"}) by (job_name, namespace)`,
expr: `group(kube_job_labels{cluster="$cluster", namespace=~"$namespace"}) by (job_name, cronjob, namespace)`,
instant: true,
format: 'table'
},
Expand Down Expand Up @@ -79,48 +85,48 @@ interface TableRow {
cluster: string;
job_name: string;
namespace: string;
Value: string;
complete: boolean;
owner: {
kind: string;
name: string;
};
}

interface TableVizState extends SceneObjectState {
expandedRows?: SceneObject[];
// asyncRowData?: Map<string, number[]>;
// asyncDataPods?: string;
asyncRowData?: Map<string, number[]>;
visibleRowIds?: string;
}

class TableViz extends SceneObjectBase<TableVizState> {

/*constructor(state: TableVizState) {
constructor(state: TableVizState) {
super({ ...state, asyncRowData: new Map<string, number[]>() });
}*/
}

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<TableViz>) => {
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: 'job_name', header: 'JOB' },
{ id: 'namespace', header: 'NAMESPACE' },
/*{ id: 'node', header: 'NODE', cell: (props: CellProps<TableRow>) => NodeCell(props.row.values.node) },
{ id: 'job_name', header: 'JOB', cell: (props: CellProps<TableRow>) => LinkCell('jobs', props.row.values.job_name) },
{ id: 'namespace', header: 'NAMESPACE' },
{ id: 'containers', header: 'CONTAINERS', cell: (props: CellProps<TableRow>) => ContainersCellBuilder(props.cell.row.id, asyncRowData) },
{ id: 'restarts', header: 'RESTARTS', cell: (props: CellProps<TableRow>) => RestartsCellBuilder(props.cell.row.id, asyncRowData) },
{ id: 'memory', header: 'MEMORY (U/R/L)', cell: (props: CellProps<TableRow>) => MemoryCellBuilder(props.cell.row.id, asyncRowData) },
{ id: 'cpu', header: 'CPU (U/R/L)', cell: (props: CellProps<TableRow>) => CPUCellBuilder(props.cell.row.id, asyncRowData) },*/
{ id: 'owner', header: 'OWNER', cell: (props: CellProps<TableRow>) => (<span>{props.row.values.owner.kind} - {props.row.values.owner.name}</span>) },
{ id: 'complete', header: 'COMPLETE', cell: (props: CellProps<TableRow>) => (<span>{props.row.values.complete ? 'Yes' : 'No'}</span>) },
],
[ /*asyncRowData*/]
[]
);

const tableData = useMemo(() => {
Expand All @@ -130,51 +136,50 @@ class TableViz extends SceneObjectBase<TableVizState> {

const frame = data.series[0];
const view = new DataFrameView<TableRow>(frame);
return view.toArray();
}, [data]);
const rows = view.toArray();

const serieMatcherPredicate = (row: TableRow) => (value: any) => value.job_name === row.job_name;

for (const row of rows) {

const complete = getSeries(asyncRowData, 'completed', serieMatcherPredicate(row))
row.complete = complete?.[`Value #completed`]

/*const onRowsChanged = (rows: any) => {
const pods = rows.map((row: any) => row.id).join('|');
const owner = getSeries(asyncRowData, 'owner', serieMatcherPredicate(row))

row.owner = {
kind: owner?.owner_kind,
name: owner?.owner_name,
}
}

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<string, number[]> = new Map<string, number[]>();
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 (
<InteractiveTable
Expand All @@ -183,7 +188,7 @@ class TableViz extends SceneObjectBase<TableVizState> {
data={tableData}
renderExpandedRow={(row) => <ExpandedRow tableViz={props.model} row={row} />}
pageSize={10}
// onRowsChanged={onRowsChanged}
onRowsChanged={onRowsChanged}
/>
);
};
Expand Down
35 changes: 35 additions & 0 deletions src/pages/Workloads/tabs/Jobs/Queries.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { SceneVariables } from "@grafana/scenes";
import { resolveVariable } from "pages/Workloads/variableHelpers";

export function createRowQueries(job: string, sceneVariables: SceneVariables) {

const cluster = resolveVariable(sceneVariables, 'cluster');

return [
{
refId: 'completed',
expr: `
max(
kube_job_complete{
job_name=~"${job}",
condition="true",
cluster="${cluster}"
}
) by (job_name)`,
instant: true,
format: 'table'
},
{
refId: 'owner',
expr: `
max(
kube_job_owner{
job_name=~"${job}",
cluster="${cluster}"
}
) by (owner_kind, owner_name, job_name)`,
instant: true,
format: 'table'
}
];
}

0 comments on commit e2149a8

Please sign in to comment.