mirror of https://github.com/linkerd/linkerd2.git
Small UI tweaks for 0.3 prep (#377)
* Display more decimal points for truncated numbers, add hover info * Filter completed pods out of web UI * Decrease the polling interval from 10s to 2s * Add more detailed pod categorization based on status * Tweak filtering of pods, tweak explanations in status table
This commit is contained in:
parent
1489a84316
commit
53354cf68f
|
@ -41,7 +41,7 @@ export default class DeploymentDetail extends React.Component {
|
|||
let deployment = urlParams.get("deploy");
|
||||
return {
|
||||
lastUpdated: 0,
|
||||
pollingInterval: 10000,
|
||||
pollingInterval: 2000,
|
||||
deploy: deployment,
|
||||
pods: [],
|
||||
upstreamMetrics: [],
|
||||
|
|
|
@ -17,7 +17,7 @@ export default class DeploymentsList extends React.Component {
|
|||
this.loadFromServer = this.loadFromServer.bind(this);
|
||||
|
||||
this.state = {
|
||||
pollingInterval: 10000, // TODO: poll based on metricsWindow size
|
||||
pollingInterval: 2000, // TODO: poll based on metricsWindow size
|
||||
metrics: [],
|
||||
pendingRequests: false,
|
||||
loaded: false,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash';
|
||||
import { metricToFormatter } from './util/Utils.js';
|
||||
import React from 'react';
|
||||
import { Table } from 'antd';
|
||||
import { Table, Tooltip } from 'antd';
|
||||
|
||||
/*
|
||||
Table to display Success Rate, Requests and Latency in tabs.
|
||||
|
@ -15,6 +15,16 @@ const resourceInfo = {
|
|||
"path": { title: "path", url: null }
|
||||
};
|
||||
|
||||
const withTooltip = (d, metricName) => {
|
||||
return (
|
||||
<Tooltip
|
||||
title={metricToFormatter["UNTRUNCATED"](d)}
|
||||
overlayStyle={{ fontSize: "12px" }}>
|
||||
<span>{metricToFormatter[metricName](d)}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const columnDefinitions = (sortable = true, resource, ConduitLink) => {
|
||||
return [
|
||||
{
|
||||
|
@ -33,7 +43,7 @@ const columnDefinitions = (sortable = true, resource, ConduitLink) => {
|
|||
key: "requestRateRollup",
|
||||
className: "numeric",
|
||||
sorter: sortable ? (a, b) => numericSort(a.requestRate, b.requestRate) : false,
|
||||
render: d => metricToFormatter["REQUEST_RATE"](d)
|
||||
render: d => withTooltip(d, "REQUEST_RATE")
|
||||
},
|
||||
{
|
||||
title: "Success Rate",
|
||||
|
|
|
@ -10,7 +10,11 @@ import React from 'react';
|
|||
import { rowGutter } from './util/Utils.js';
|
||||
import StatusTable from './StatusTable.jsx';
|
||||
import { Col, Row, Table } from 'antd';
|
||||
import { processRollupMetrics, processTimeseriesMetrics } from './util/MetricUtils.js';
|
||||
import {
|
||||
getPodsByDeployment,
|
||||
processRollupMetrics,
|
||||
processTimeseriesMetrics
|
||||
} from './util/MetricUtils.js';
|
||||
import './../../css/service-mesh.css';
|
||||
|
||||
const serviceMeshDetailsColumns = [
|
||||
|
@ -99,14 +103,14 @@ export default class ServiceMesh extends React.Component {
|
|||
.then(([metrics, ts, pods]) => {
|
||||
let m = processRollupMetrics(metrics.metrics, "component");
|
||||
let tsByComponent = processTimeseriesMetrics(ts.metrics, "component");
|
||||
let d = this.getDeploymentList(pods.pods);
|
||||
let c = this.processComponents(pods.pods);
|
||||
let podsByDeploy = getPodsByDeployment(pods.pods);
|
||||
let controlPlanePods = this.processComponents(pods.pods);
|
||||
|
||||
this.setState({
|
||||
metrics: m,
|
||||
timeseriesByComponent: tsByComponent,
|
||||
deploys: d,
|
||||
components: c,
|
||||
deploys: podsByDeploy,
|
||||
components: controlPlanePods,
|
||||
lastUpdated: Date.now(),
|
||||
pendingRequests: false,
|
||||
loaded: true,
|
||||
|
@ -124,7 +128,7 @@ export default class ServiceMesh extends React.Component {
|
|||
|
||||
addedDeploymentCount() {
|
||||
return _.size(_.filter(this.state.deploys, d => {
|
||||
return _.every(d.pods, ["value", "good"]);
|
||||
return _.every(d.pods, ["added", true]);
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -157,23 +161,6 @@ export default class ServiceMesh extends React.Component {
|
|||
];
|
||||
}
|
||||
|
||||
getDeploymentList(pods) {
|
||||
return _(pods)
|
||||
.reject(p => _.isEmpty(p.deployment) || p.controlPlane)
|
||||
.groupBy("deployment")
|
||||
.map((componentPods, name) => {
|
||||
_.remove(componentPods, p => {
|
||||
return p.status === "Terminating";
|
||||
});
|
||||
let podStatuses = _.map(componentPods, p => {
|
||||
return { name: p.name, value: p.added ? "good" : "neutral" };
|
||||
});
|
||||
return { name: name, pods: _.sortBy(podStatuses, "name") };
|
||||
})
|
||||
.sortBy("name")
|
||||
.value();
|
||||
}
|
||||
|
||||
processComponents(pods) {
|
||||
let podIndex = _(pods)
|
||||
.filter(p => p.controlPlane)
|
||||
|
|
|
@ -6,17 +6,20 @@ const columnConfig = {
|
|||
"Pod Status": {
|
||||
width: 200,
|
||||
wrapDotsAt: 7, // dots take up more than one line in the table; space them out
|
||||
dotExplanation: {
|
||||
good: "is up and running",
|
||||
neutral: "has not been started"
|
||||
dotExplanation: status => {
|
||||
return status.value === "good" ? "is up and running" : "has not been started";
|
||||
}
|
||||
},
|
||||
"Proxy Status": {
|
||||
width: 250,
|
||||
wrapDotsAt: 9,
|
||||
dotExplanation: {
|
||||
good: "has been added to the mesh",
|
||||
neutral: "has not been added to the mesh"
|
||||
dotExplanation: pod => {
|
||||
let addedStatus = !pod.added ? "Not in mesh" : "Added to mesh";
|
||||
|
||||
return (<React.Fragment>
|
||||
<div>Pod status: {pod.status}</div>
|
||||
<div>{addedStatus}</div>
|
||||
</React.Fragment>);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -26,7 +29,7 @@ const StatusDot = ({status, multilineDots, columnName}) => (
|
|||
placement="top"
|
||||
title={<div>
|
||||
<div>{status.name}</div>
|
||||
<div>{_.get(columnConfig, [columnName, "dotExplanation", status.value])}</div>
|
||||
<div>{_.get(columnConfig, [columnName, "dotExplanation"])(status)}</div>
|
||||
</div>}
|
||||
overlayStyle={{ fontSize: "12px" }}>
|
||||
<div
|
||||
|
|
|
@ -26,17 +26,36 @@ const convertLatencyTs = rawTs => {
|
|||
return _.groupBy(latencies, 'label');
|
||||
};
|
||||
|
||||
const getPodCategorization = pod => {
|
||||
if (pod.added && pod.status === "Running") {
|
||||
return "good";
|
||||
} else if (pod.status === "Pending" || pod.status === "Running") {
|
||||
return "neutral";
|
||||
} else if (pod.status === "Failed") {
|
||||
return "bad";
|
||||
}
|
||||
return ""; // Terminating | Succeeded | Unknown
|
||||
};
|
||||
|
||||
export const getPodsByDeployment = pods => {
|
||||
return _(pods)
|
||||
.reject(p => _.isEmpty(p.deployment) || p.controlPlane)
|
||||
.groupBy('deployment')
|
||||
.map((componentPods, name) => {
|
||||
let podsWithStatus = _.chain(componentPods)
|
||||
.map(p => {
|
||||
return _.merge({}, p, { value: getPodCategorization(p) });
|
||||
})
|
||||
.reject(p => _.isEmpty(p.value))
|
||||
.value();
|
||||
|
||||
return {
|
||||
name: name,
|
||||
added: _.every(componentPods, 'added'),
|
||||
pods: componentPods
|
||||
pods: _.sortBy(podsWithStatus, 'name')
|
||||
};
|
||||
})
|
||||
.reject(p => _.isEmpty(p.pods))
|
||||
.sortBy('name')
|
||||
.value();
|
||||
};
|
||||
|
|
|
@ -27,7 +27,8 @@ const formatLatency = m => {
|
|||
export const metricToFormatter = {
|
||||
"REQUEST_RATE": m => _.isNil(m) ? "---" : styleNum(m, " RPS", true),
|
||||
"SUCCESS_RATE": m => _.isNil(m) ? "---" : successRateFormatter(m),
|
||||
"LATENCY": formatLatency
|
||||
"LATENCY": formatLatency,
|
||||
"UNTRUNCATED": m => styleNum(m, "", false)
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -61,13 +62,13 @@ export const styleNum = (number, unit = "", truncate = true) => {
|
|||
}
|
||||
|
||||
if (truncate && number > 999999999) {
|
||||
number = roundNumber(number / 1000000000.0, 1);
|
||||
number = roundNumber(number / 1000000000.0, 3);
|
||||
return addCommas(number) + "G" + unit;
|
||||
} else if (truncate && number > 999999) {
|
||||
number = roundNumber(number / 1000000.0, 1);
|
||||
number = roundNumber(number / 1000000.0, 3);
|
||||
return addCommas(number) + "M" + unit;
|
||||
} else if (truncate && number > 999) {
|
||||
number = roundNumber(number / 1000.0, 1);
|
||||
number = roundNumber(number / 1000.0, 3);
|
||||
return addCommas(number) + "k" + unit;
|
||||
} else if (number > 999) {
|
||||
number = roundNumber(number, 0);
|
||||
|
|
|
@ -67,7 +67,7 @@ describe('ServiceMesh', () => {
|
|||
|
||||
it("renders controller component summaries", () => {
|
||||
let addedPods = _.cloneDeep(podFixtures.pods);
|
||||
_.set(addedPods[0], "added", true);
|
||||
_.set(addedPods[1], "added", true);
|
||||
|
||||
fetchStub.resolves({
|
||||
ok: true,
|
||||
|
|
|
@ -17,16 +17,16 @@ describe('Utils', () => {
|
|||
compare( 5.0000001, "5" );
|
||||
compare( 7.6666667, "7.67" );
|
||||
compare( 123.456, "123.46" );
|
||||
compare( 1212.999999, "1.2k" );
|
||||
compare( 5329.333333, "5.3k" );
|
||||
compare( 16384.888, "16.4k" );
|
||||
compare( 131042, "131k" );
|
||||
compare( 1048576, "1M" );
|
||||
compare( 2097152.1, "2.1M" );
|
||||
compare( 16777216, "16.8M" );
|
||||
compare( 536870912, "536.9M" );
|
||||
compare( 1073741824, "1.1G" );
|
||||
compare(68719476736, "68.7G" );
|
||||
compare( 1212.999999, "1.213k" );
|
||||
compare( 5329.333333, "5.329k" );
|
||||
compare( 16384.888, "16.385k" );
|
||||
compare( 131042, "131.042k");
|
||||
compare( 1048576, "1.049M" );
|
||||
compare( 2097152.1, "2.097M" );
|
||||
compare( 16777216, "16.777M" );
|
||||
compare( 536870912, "536.871M");
|
||||
compare( 1073741824, "1.074G" );
|
||||
compare(68719476736, "68.719G" );
|
||||
});
|
||||
|
||||
it('properly formats numbers with units and no truncation', () => {
|
||||
|
@ -63,9 +63,9 @@ describe('Utils', () => {
|
|||
expect(metricToFormatter["REQUEST_RATE"](99)).to.equal('99 RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](999)).to.equal('999 RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](1000)).to.equal('1k RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](4444)).to.equal('4.4k RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](9999)).to.equal('10k RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](99999)).to.equal('100k RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](4444)).to.equal('4.444k RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](9999)).to.equal('9.999k RPS');
|
||||
expect(metricToFormatter["REQUEST_RATE"](99999)).to.equal('99.999k RPS');
|
||||
});
|
||||
|
||||
it('formats subsecond latency as ms', () => {
|
||||
|
|
Loading…
Reference in New Issue