Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan

### Added

- [#45](https://github.com/kobsio/kobs/pull/45): Add value mappings for `sparkline` charts in the Prometheus plugin.

### Fixed

- [#43](https://github.com/kobsio/kobs/pull/43): Fix `hosts` and `gateways` list for VirtualService in the Helm chart.
Expand Down
11 changes: 6 additions & 5 deletions app/src/plugins/prometheus/PrometheusChartDefault.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import {
import React, { useEffect, useRef, useState } from 'react';

import { Data, Metrics } from 'proto/prometheus_grpc_web_pb';
import { IData, transformData } from 'plugins/prometheus/helpers';
import { formatTime } from 'utils/helpers';

interface ILabels {
datum: Data.AsObject;
datum: IData;
}

export interface IPrometheusChartDefaultProps {
Expand Down Expand Up @@ -63,11 +64,11 @@ const PrometheusChartDefault: React.FunctionComponent<IPrometheusChartDefaultPro
const legendData = metrics.map((metric, index) => ({ childName: `index${index}`, name: metric.label }));
const series = metrics.map((metric, index) =>
type === 'area' ? (
<ChartArea key={index} data={metric.dataList} interpolation="monotoneX" name={`index${index}`} />
<ChartArea key={index} data={transformData(metric.dataList)} interpolation="monotoneX" name={`index${index}`} />
) : type === 'bar' ? (
<ChartBar key={index} data={metric.dataList} name={`index${index}`} />
<ChartBar key={index} data={transformData(metric.dataList)} name={`index${index}`} />
) : (
<ChartLine key={index} data={metric.dataList} interpolation="monotoneX" name={`index${index}`} />
<ChartLine key={index} data={transformData(metric.dataList)} interpolation="monotoneX" name={`index${index}`} />
),
);

Expand All @@ -77,7 +78,7 @@ const PrometheusChartDefault: React.FunctionComponent<IPrometheusChartDefaultPro
containerComponent={
<CursorVoronoiContainer
cursorDimension="x"
labels={({ datum }: ILabels): string => `${datum.y} ${unit}`}
labels={({ datum }: ILabels): string => (datum.y ? `${datum.y} ${unit}` : 'N/A')}
labelComponent={
<ChartLegendTooltip
legendData={legendData}
Expand Down
37 changes: 31 additions & 6 deletions app/src/plugins/prometheus/PrometheusChartSparkline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,22 @@ import { ChartArea, ChartGroup } from '@patternfly/react-charts';
import React, { useEffect, useRef, useState } from 'react';

import { Metrics } from 'proto/prometheus_grpc_web_pb';
import { transformData } from 'plugins/prometheus/helpers';

const getMappingValue = (value: number, mappings: [string, string][]): string => {
for (const mapEntry of mappings) {
if (mapEntry[0] === value.toString()) {
return mapEntry[1];
}
}

return value.toString();
};

export interface IPrometheusChartSparklineProps {
unit: string;
metrics: Metrics.AsObject[];
mappings: [string, string][];
}

// PrometheusChartSparkline displays a sparkline chart. The complete documentation for sparklines can be found in the
Expand All @@ -14,6 +26,7 @@ export interface IPrometheusChartSparklineProps {
const PrometheusChartSparkline: React.FunctionComponent<IPrometheusChartSparklineProps> = ({
unit,
metrics,
mappings,
}: IPrometheusChartSparklineProps) => {
const refChart = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState<number>(0);
Expand All @@ -33,16 +46,28 @@ const PrometheusChartSparkline: React.FunctionComponent<IPrometheusChartSparklin
return null;
}

let label = `${metrics[0].dataList[metrics[0].dataList.length - 1].y} ${unit}`;
if (mappings.length > 0) {
label = getMappingValue(metrics[0].dataList[metrics[0].dataList.length - 1].y, mappings);
}

return (
<div style={{ height: '150px', position: 'relative', width: '100%' }} ref={refChart}>
<div style={{ fontSize: '24px', position: 'absolute', textAlign: 'center', top: '63px', width: '100%' }}>
{metrics[0].dataList[metrics[0].dataList.length - 1].y} {unit}
{label}
</div>
<ChartGroup height={height} padding={0} width={width}>
{metrics.map((metric, index) => (
<ChartArea key={index} data={metric.dataList} interpolation="monotoneX" name={`index${index}`} />
))}
</ChartGroup>
{mappings.length === 0 ? (
<ChartGroup height={height} padding={0} width={width}>
{metrics.map((metric, index) => (
<ChartArea
key={index}
data={transformData(metric.dataList)}
interpolation="monotoneX"
name={`index${index}`}
/>
))}
</ChartGroup>
) : null}
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion app/src/plugins/prometheus/PrometheusPluginChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ const PrometheusPluginChart: React.FunctionComponent<IPrometheusPluginChartProps
<p>{data.error}</p>
</Alert>
) : chart.type === 'sparkline' ? (
<PrometheusChartSparkline unit={chart.unit} metrics={data.metrics} />
<PrometheusChartSparkline unit={chart.unit} metrics={data.metrics} mappings={chart.mappingsMap} />
) : (
<PrometheusChartDefault type={chart.type} unit={chart.unit} stacked={chart.stacked} metrics={data.metrics} />
)}
Expand Down
16 changes: 15 additions & 1 deletion app/src/plugins/prometheus/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Chart, Query, Spec, Variable } from 'proto/prometheus_grpc_web_pb';
import { Chart, Data, Query, Spec, Variable } from 'proto/prometheus_grpc_web_pb';
import { Plugin } from 'proto/plugins_grpc_web_pb';

// ITimes is the interface for a start and end time.
Expand Down Expand Up @@ -106,3 +106,17 @@ export const jsonToProto = (json: any): Plugin.AsObject | undefined => {

return plugin.toObject();
};

export interface IData {
x: number;
y: number | null;
}

// transformData is used to transform the returned data from the API. This is needed because, the API returns a NaN
// value for missing values, but Victory requires a null value.
// See: https://formidable.com/open-source/victory/gallery/victory-line-with-null-data/
export const transformData = (data: Data.AsObject[]): IData[] => {
return data.map((d) => {
return { x: d.x, y: isNaN(d.y) ? null : d.y };
});
};
7 changes: 7 additions & 0 deletions app/src/proto/prometheus_pb.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ export class Metrics extends jspb.Message {
getMax(): number;
setMax(value: number): void;

getAvg(): number;
setAvg(value: number): void;

clearDataList(): void;
getDataList(): Array<Data>;
setDataList(value: Array<Data>): void;
Expand All @@ -211,6 +214,7 @@ export namespace Metrics {
label: string,
min: number,
max: number,
avg: number,
dataList: Array<Data.AsObject>,
}
}
Expand Down Expand Up @@ -325,6 +329,8 @@ export class Chart extends jspb.Message {
getSize(): number;
setSize(value: number): void;

getMappingsMap(): jspb.Map<string, string>;
clearMappingsMap(): void;
clearQueriesList(): void;
getQueriesList(): Array<Query>;
setQueriesList(value: Array<Query>): void;
Expand All @@ -347,6 +353,7 @@ export namespace Chart {
unit: string,
stacked: boolean,
size: number,
mappingsMap: Array<[string, string]>,
queriesList: Array<Query.AsObject>,
}
}
Expand Down
87 changes: 75 additions & 12 deletions app/src/proto/prometheus_pb.js
Original file line number Diff line number Diff line change
Expand Up @@ -1582,7 +1582,7 @@ proto.plugins.prometheus.MetricLookupResponse.prototype.clearNamesList = functio
* @private {!Array<number>}
* @const
*/
proto.plugins.prometheus.Metrics.repeatedFields_ = [4];
proto.plugins.prometheus.Metrics.repeatedFields_ = [5];



Expand Down Expand Up @@ -1618,6 +1618,7 @@ proto.plugins.prometheus.Metrics.toObject = function(includeInstance, msg) {
label: jspb.Message.getFieldWithDefault(msg, 1, ""),
min: jspb.Message.getFloatingPointFieldWithDefault(msg, 2, 0.0),
max: jspb.Message.getFloatingPointFieldWithDefault(msg, 3, 0.0),
avg: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0),
dataList: jspb.Message.toObjectList(msg.getDataList(),
proto.plugins.prometheus.Data.toObject, includeInstance)
};
Expand Down Expand Up @@ -1669,6 +1670,10 @@ proto.plugins.prometheus.Metrics.deserializeBinaryFromReader = function(msg, rea
msg.setMax(value);
break;
case 4:
var value = /** @type {number} */ (reader.readDouble());
msg.setAvg(value);
break;
case 5:
var value = new proto.plugins.prometheus.Data;
reader.readMessage(value,proto.plugins.prometheus.Data.deserializeBinaryFromReader);
msg.addData(value);
Expand Down Expand Up @@ -1723,10 +1728,17 @@ proto.plugins.prometheus.Metrics.serializeBinaryToWriter = function(message, wri
f
);
}
f = message.getAvg();
if (f !== 0.0) {
writer.writeDouble(
4,
f
);
}
f = message.getDataList();
if (f.length > 0) {
writer.writeRepeatedMessage(
4,
5,
f,
proto.plugins.prometheus.Data.serializeBinaryToWriter
);
Expand Down Expand Up @@ -1789,12 +1801,30 @@ proto.plugins.prometheus.Metrics.prototype.setMax = function(value) {


/**
* repeated Data data = 4;
* optional double avg = 4;
* @return {number}
*/
proto.plugins.prometheus.Metrics.prototype.getAvg = function() {
return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 4, 0.0));
};


/**
* @param {number} value
* @return {!proto.plugins.prometheus.Metrics} returns this
*/
proto.plugins.prometheus.Metrics.prototype.setAvg = function(value) {
return jspb.Message.setProto3FloatField(this, 4, value);
};


/**
* repeated Data data = 5;
* @return {!Array<!proto.plugins.prometheus.Data>}
*/
proto.plugins.prometheus.Metrics.prototype.getDataList = function() {
return /** @type{!Array<!proto.plugins.prometheus.Data>} */ (
jspb.Message.getRepeatedWrapperField(this, proto.plugins.prometheus.Data, 4));
jspb.Message.getRepeatedWrapperField(this, proto.plugins.prometheus.Data, 5));
};


Expand All @@ -1803,7 +1833,7 @@ proto.plugins.prometheus.Metrics.prototype.getDataList = function() {
* @return {!proto.plugins.prometheus.Metrics} returns this
*/
proto.plugins.prometheus.Metrics.prototype.setDataList = function(value) {
return jspb.Message.setRepeatedWrapperField(this, 4, value);
return jspb.Message.setRepeatedWrapperField(this, 5, value);
};


Expand All @@ -1813,7 +1843,7 @@ proto.plugins.prometheus.Metrics.prototype.setDataList = function(value) {
* @return {!proto.plugins.prometheus.Data}
*/
proto.plugins.prometheus.Metrics.prototype.addData = function(opt_value, opt_index) {
return jspb.Message.addToRepeatedWrapperField(this, 4, opt_value, proto.plugins.prometheus.Data, opt_index);
return jspb.Message.addToRepeatedWrapperField(this, 5, opt_value, proto.plugins.prometheus.Data, opt_index);
};


Expand Down Expand Up @@ -2511,7 +2541,7 @@ proto.plugins.prometheus.Variable.prototype.setValue = function(value) {
* @private {!Array<number>}
* @const
*/
proto.plugins.prometheus.Chart.repeatedFields_ = [6];
proto.plugins.prometheus.Chart.repeatedFields_ = [7];



Expand Down Expand Up @@ -2549,6 +2579,7 @@ proto.plugins.prometheus.Chart.toObject = function(includeInstance, msg) {
unit: jspb.Message.getFieldWithDefault(msg, 3, ""),
stacked: jspb.Message.getBooleanFieldWithDefault(msg, 4, false),
size: jspb.Message.getFieldWithDefault(msg, 5, 0),
mappingsMap: (f = msg.getMappingsMap()) ? f.toObject(includeInstance, undefined) : [],
queriesList: jspb.Message.toObjectList(msg.getQueriesList(),
proto.plugins.prometheus.Query.toObject, includeInstance)
};
Expand Down Expand Up @@ -2608,6 +2639,12 @@ proto.plugins.prometheus.Chart.deserializeBinaryFromReader = function(msg, reade
msg.setSize(value);
break;
case 6:
var value = msg.getMappingsMap();
reader.readMessage(value, function(message, reader) {
jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readString, null, "", "");
});
break;
case 7:
var value = new proto.plugins.prometheus.Query;
reader.readMessage(value,proto.plugins.prometheus.Query.deserializeBinaryFromReader);
msg.addQueries(value);
Expand Down Expand Up @@ -2676,10 +2713,14 @@ proto.plugins.prometheus.Chart.serializeBinaryToWriter = function(message, write
f
);
}
f = message.getMappingsMap(true);
if (f && f.getLength() > 0) {
f.serializeBinary(6, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString);
}
f = message.getQueriesList();
if (f.length > 0) {
writer.writeRepeatedMessage(
6,
7,
f,
proto.plugins.prometheus.Query.serializeBinaryToWriter
);
Expand Down Expand Up @@ -2778,12 +2819,34 @@ proto.plugins.prometheus.Chart.prototype.setSize = function(value) {


/**
* repeated Query queries = 6;
* map<string, string> mappings = 6;
* @param {boolean=} opt_noLazyCreate Do not create the map if
* empty, instead returning `undefined`
* @return {!jspb.Map<string,string>}
*/
proto.plugins.prometheus.Chart.prototype.getMappingsMap = function(opt_noLazyCreate) {
return /** @type {!jspb.Map<string,string>} */ (
jspb.Message.getMapField(this, 6, opt_noLazyCreate,
null));
};


/**
* Clears values from the map. The map will be non-null.
* @return {!proto.plugins.prometheus.Chart} returns this
*/
proto.plugins.prometheus.Chart.prototype.clearMappingsMap = function() {
this.getMappingsMap().clear();
return this;};


/**
* repeated Query queries = 7;
* @return {!Array<!proto.plugins.prometheus.Query>}
*/
proto.plugins.prometheus.Chart.prototype.getQueriesList = function() {
return /** @type{!Array<!proto.plugins.prometheus.Query>} */ (
jspb.Message.getRepeatedWrapperField(this, proto.plugins.prometheus.Query, 6));
jspb.Message.getRepeatedWrapperField(this, proto.plugins.prometheus.Query, 7));
};


Expand All @@ -2792,7 +2855,7 @@ proto.plugins.prometheus.Chart.prototype.getQueriesList = function() {
* @return {!proto.plugins.prometheus.Chart} returns this
*/
proto.plugins.prometheus.Chart.prototype.setQueriesList = function(value) {
return jspb.Message.setRepeatedWrapperField(this, 6, value);
return jspb.Message.setRepeatedWrapperField(this, 7, value);
};


Expand All @@ -2802,7 +2865,7 @@ proto.plugins.prometheus.Chart.prototype.setQueriesList = function(value) {
* @return {!proto.plugins.prometheus.Query}
*/
proto.plugins.prometheus.Chart.prototype.addQueries = function(opt_value, opt_index) {
return jspb.Message.addToRepeatedWrapperField(this, 6, opt_value, proto.plugins.prometheus.Query, opt_index);
return jspb.Message.addToRepeatedWrapperField(this, 7, opt_value, proto.plugins.prometheus.Query, opt_index);
};


Expand Down
2 changes: 1 addition & 1 deletion deploy/helm/kobs/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ description: Kubernetes Observability Platform
type: application
home: https://kobs.io
icon: https://kobs.io/assets/images/logo.svg
version: 0.3.1
version: 0.3.2
appVersion: v0.2.0
Loading