diff --git a/web/app/css/styles.css b/web/app/css/styles.css
index 03db3162d..d6523d28c 100644
--- a/web/app/css/styles.css
+++ b/web/app/css/styles.css
@@ -241,6 +241,19 @@ a.button.primary:active {
padding-right: 0;
}
+ & tr > th.long-header > span {
+ display: inline-block;
+ margin-right: 0;
+
+ & div {
+ float: left;
+
+ &.ant-table-column-sorter {
+ padding-top: 12px;
+ }
+ }
+ }
+
& .ant-table-tbody > tr:hover > td {
background: rgba(47, 128, 237, .1);
}
diff --git a/web/app/js/components/GrafanaLink.jsx b/web/app/js/components/GrafanaLink.jsx
index a788f72be..f45eb53a4 100644
--- a/web/app/js/components/GrafanaLink.jsx
+++ b/web/app/js/components/GrafanaLink.jsx
@@ -4,15 +4,13 @@ export default class GrafanaLink extends React.Component {
render() {
let resourceVariableName = this.props.resource.toLowerCase().replace(" ", "_");
let dashboardName = this.props.resource.toLowerCase().replace(" ", "-");
- let ownerInfo = this.props.name.split("/");
- let namespace = ownerInfo[0];
- let name = ownerInfo[1];
+
return (
- {this.props.name}
+ {this.props.displayName || this.props.name}
);
}
diff --git a/web/app/js/components/MetricsTable.jsx b/web/app/js/components/MetricsTable.jsx
index 0a53850a5..86a53d198 100644
--- a/web/app/js/components/MetricsTable.jsx
+++ b/web/app/js/components/MetricsTable.jsx
@@ -20,54 +20,76 @@ const withTooltip = (d, metricName) => {
);
};
+const narrowColumnWidth = 100;
+const formatLongTitles = title => {
+ let words = title.split(" ");
+ if (words.length === 2) {
+ return (
{words[0]}
{words[1]}
);
+ } else {
+ return words;
+ }
+};
const columnDefinitions = (sortable = true, resource, ConduitLink) => {
return [
+ {
+ title: "Namespace",
+ key: "namespace",
+ dataIndex: "namespace",
+ sorter: sortable ? (a, b) => (a.namespace || "").localeCompare(b.namespace) : false
+ },
{
title: resource,
key: "name",
defaultSortOrder: 'ascend',
- width: 150,
sorter: sortable ? (a, b) => (a.name || "").localeCompare(b.name) : false,
- render: row => row.added ?
- : row.name
+ render: row => row.added ? : row.name
},
{
- title: "Success Rate",
+ title: formatLongTitles("Success Rate"),
dataIndex: "successRate",
key: "successRateRollup",
- className: "numeric",
+ className: "numeric long-header",
+ width: narrowColumnWidth,
sorter: sortable ? (a, b) => numericSort(a.successRate, b.successRate) : false,
render: d => metricToFormatter["SUCCESS_RATE"](d)
},
{
- title: "Request Rate",
+ title: formatLongTitles("Request Rate"),
dataIndex: "requestRate",
key: "requestRateRollup",
- className: "numeric",
+ className: "numeric long-header",
+ width: narrowColumnWidth,
sorter: sortable ? (a, b) => numericSort(a.requestRate, b.requestRate) : false,
render: d => withTooltip(d, "REQUEST_RATE")
},
{
- title: "P50 Latency",
+ title: formatLongTitles("P50 Latency"),
dataIndex: "P50",
key: "p50LatencyRollup",
- className: "numeric",
+ className: "numeric long-header",
+ width: narrowColumnWidth,
sorter: sortable ? (a, b) => numericSort(a.P50, b.P50) : false,
render: metricToFormatter["LATENCY"]
},
{
- title: "P95 Latency",
+ title: formatLongTitles("P95 Latency"),
dataIndex: "P95",
key: "p95LatencyRollup",
- className: "numeric",
+ className: "numeric long-header",
+ width: narrowColumnWidth,
sorter: sortable ? (a, b) => numericSort(a.P95, b.P95) : false,
render: metricToFormatter["LATENCY"]
},
{
- title: "P99 Latency",
+ title: formatLongTitles("P99 Latency"),
dataIndex: "P99",
key: "p99LatencyRollup",
- className: "numeric",
+ className: "numeric long-header",
+ width: narrowColumnWidth,
sorter: sortable ? (a, b) => numericSort(a.P99, b.P99) : false,
render: metricToFormatter["LATENCY"]
}
diff --git a/web/app/js/components/PodsList.jsx b/web/app/js/components/PodsList.jsx
index 72b5ea5e3..fce657d63 100644
--- a/web/app/js/components/PodsList.jsx
+++ b/web/app/js/components/PodsList.jsx
@@ -34,17 +34,6 @@ export default class PodsList extends React.Component {
this.api.cancelCurrentRequests();
}
- filterPods(pods, metrics) {
- let podsByName = _.keyBy(pods, 'name');
- return _.compact(_.map(metrics, metric => {
- let pod = podsByName[metric.name];
- if (pod && !pod.controlPlane) {
- metric.added = pod.added;
- return metric;
- }
- }));
- }
-
loadFromServer() {
if (this.state.pendingRequests) {
return; // don't make more requests if the ones we sent haven't completed
@@ -52,17 +41,15 @@ export default class PodsList extends React.Component {
this.setState({ pendingRequests: true });
this.api.setCurrentRequests([
- this.api.fetchMetrics(this.api.urlsForResource["pod"].url().rollup),
- this.api.fetchPods()
+ this.api.fetchMetrics(this.api.urlsForResource["pod"].url().rollup)
]);
Promise.all(this.api.getCurrentPromises())
- .then(([rollup, pods]) => {
- let meshPods = processRollupMetrics(rollup);
- let combinedMetrics = this.filterPods(pods.pods, meshPods);
+ .then(([rollup]) => {
+ let processedMetrics = processRollupMetrics(rollup);
this.setState({
- metrics: combinedMetrics,
+ metrics: processedMetrics,
loaded: true,
pendingRequests: false,
error: ''
diff --git a/web/app/js/components/StatusTable.jsx b/web/app/js/components/StatusTable.jsx
index 0f0e1551c..c61395399 100644
--- a/web/app/js/components/StatusTable.jsx
+++ b/web/app/js/components/StatusTable.jsx
@@ -44,8 +44,16 @@ const columns = {
return {
title: "Deployment",
key: "name",
- render: row => shouldLink && row.added ?
- : row.name
+ render: row => {
+ let ownerInfo = row.name.split("/");
+ return shouldLink && row.added ?
+ : row.name;
+ }
};
},
pods: {
diff --git a/web/app/js/components/util/MetricUtils.js b/web/app/js/components/util/MetricUtils.js
index d8e14594a..8ad8254c8 100644
--- a/web/app/js/components/util/MetricUtils.js
+++ b/web/app/js/components/util/MetricUtils.js
@@ -107,7 +107,8 @@ export const processRollupMetrics = (rawMetrics, controllerNamespace) => {
return null;
}
return {
- name: row.resource.namespace + "/" + row.resource.name,
+ name: row.resource.name,
+ namespace: row.resource.namespace,
requestRate: getRequestRate(row),
successRate: getSuccessRate(row),
latency: getLatency(row),
diff --git a/web/app/test/GrafanaLinkTest.jsx b/web/app/test/GrafanaLinkTest.jsx
new file mode 100644
index 000000000..aacfeee5a
--- /dev/null
+++ b/web/app/test/GrafanaLinkTest.jsx
@@ -0,0 +1,34 @@
+import Adapter from 'enzyme-adapter-react-16';
+import { ApiHelpers } from '../js/components/util/ApiHelpers.jsx';
+import Enzyme from 'enzyme';
+import { expect } from 'chai';
+import GrafanaLink from '../js/components/GrafanaLink.jsx';
+import { mount } from 'enzyme';
+import { routerWrap } from './testHelpers.jsx';
+import sinon from 'sinon';
+import sinonStubPromise from 'sinon-stub-promise';
+
+Enzyme.configure({ adapter: new Adapter() });
+sinonStubPromise(sinon);
+
+describe('GrafanaLink', () => {
+ it('makes a link', () => {
+ let api = ApiHelpers('');
+ let linkProps = {
+ resource: "Replication Controller",
+ name: "aldksf-3409823049823",
+ namespace: "myns",
+ conduitLink: api.ConduitLink
+ };
+ let component = mount(routerWrap(GrafanaLink, linkProps));
+
+ let expectedDashboardNameStr = "/conduit-replication-controller";
+ let expectedNsStr = "var-namespace=myns";
+ let expectedVarNameStr = "var-replication_controller=aldksf-3409823049823";
+
+ expect(component.find("GrafanaLink")).to.have.length(1);
+ expect(component.html()).to.contain(expectedDashboardNameStr);
+ expect(component.html()).to.contain(expectedNsStr);
+ expect(component.html()).to.contain(expectedVarNameStr);
+ });
+});
diff --git a/web/app/test/MetricUtilsTest.js b/web/app/test/MetricUtilsTest.js
index 34ad78492..3da89aeba 100644
--- a/web/app/test/MetricUtilsTest.js
+++ b/web/app/test/MetricUtilsTest.js
@@ -9,7 +9,8 @@ describe('MetricUtils', () => {
let result = processRollupMetrics(deployRollupFixtures);
let expectedResult = [
{
- name: 'emojivoto/voting',
+ name: 'voting',
+ namespace: 'emojivoto',
requestRate: 2.5,
successRate: 0.9,
latency: {
@@ -26,10 +27,14 @@ describe('MetricUtils', () => {
it('Extracts and sorts multiple deploys from a single response', () => {
let result = processRollupMetrics(multiDeployRollupFixtures);
expect(result).to.have.length(4);
- expect(result[0].name).to.equal("emojivoto/emoji");
- expect(result[1].name).to.equal("emojivoto/vote-bot");
- expect(result[2].name).to.equal("emojivoto/voting");
- expect(result[3].name).to.equal("emojivoto/web");
+ expect(result[0].name).to.equal("emoji");
+ expect(result[0].namespace).to.equal("emojivoto");
+ expect(result[1].name).to.equal("vote-bot");
+ expect(result[1].namespace).to.equal("emojivoto");
+ expect(result[2].name).to.equal("voting");
+ expect(result[2].namespace).to.equal("emojivoto");
+ expect(result[3].name).to.equal("web");
+ expect(result[3].namespace).to.equal("emojivoto");
});
});
});