From 0ed40288e592c0a1661dc948cdc847df54af7e1a Mon Sep 17 00:00:00 2001 From: Risha Mars Date: Fri, 15 Jun 2018 14:12:36 -0700 Subject: [PATCH] Display proxy container errors in the Web UI (#1130) * Display proxy container errors in the Web UI Add an error modal to display pod errors Add icon to data tables to indicate errors are present Display errors on the Service Mesh Overview Page and all the resource pages --- web/app/css/service-mesh.css | 25 +++++++++ web/app/js/components/ErrorModal.jsx | 68 +++++++++++++++++++++++ web/app/js/components/MetricsTable.jsx | 14 ++++- web/app/js/components/ServiceMesh.jsx | 19 +++++-- web/app/js/components/util/MetricUtils.js | 3 +- web/app/test/MetricUtilsTest.js | 3 +- web/app/test/fixtures/deployRollup.json | 3 +- 7 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 web/app/js/components/ErrorModal.jsx diff --git a/web/app/css/service-mesh.css b/web/app/css/service-mesh.css index 1bab79ac6..48ab78e79 100644 --- a/web/app/css/service-mesh.css +++ b/web/app/css/service-mesh.css @@ -85,3 +85,28 @@ td .status-dot { background-color: #E0E0E0; } } + + +/* error indicator and modal */ +.conduit-error-icon { + cursor: pointer; + margin-left: 5px; + color: var(--siennared); +} + +.conduit-pod-error { + margin-top: calc(2 * var(--base-width)); + margin-bottom: calc(3 * var(--base-width)); + + & p { + line-height: 8px; + } + + & .error-text { + padding: var(--base-width); + border-radius: calc(0.5 * var(--base-width)); + font-size: 12px; + color: white; + background-color: #696969; + } +} diff --git a/web/app/js/components/ErrorModal.jsx b/web/app/js/components/ErrorModal.jsx new file mode 100644 index 000000000..bd4593831 --- /dev/null +++ b/web/app/js/components/ErrorModal.jsx @@ -0,0 +1,68 @@ +import _ from 'lodash'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { Icon, Modal } from 'antd'; + +export default class ErrorModal extends React.Component { + static propTypes = { + errors: PropTypes.shape({}).isRequired, + resourceName: PropTypes.string.isRequired, + resourceType: PropTypes.string.isRequired + } + + showModal = () => { + Modal.error({ + title: `Errors in ${this.props.resourceType} ${this.props.resourceName}`, + width: 800, + maskClosable: true, + content: this.renderPodErrors(this.props.errors), + onOk() {}, + }); + } + + renderContainerErrors = errorsByContainer => { + return _.map(errorsByContainer, (errors, container) => ( +
+

Container: {container}

+

Image: {_.get(errors, [0, "image"])}

+
+ { + _.map(errors, (er, i) => + {er.message} + ) + } +
+
+ )); + }; + + renderPodErrors = podErrors => { + let errorsByPodAndContainer = _(podErrors) + .keys() + .sortBy() + .map(pod => { + return { + pod: pod, + byContainer: _(podErrors[pod].errors) + .groupBy( "container.container") + .mapValues(v => _.map(v, "container")) + .value() + }; + }).value(); + + return _.map(errorsByPodAndContainer, err => { + return ( +
+

Pod: {err.pod}

+ {this.renderContainerErrors(err.byContainer)} +
+ ); + }); + } + + render() { + return ( + + ); + } +} diff --git a/web/app/js/components/MetricsTable.jsx b/web/app/js/components/MetricsTable.jsx index ad3a6883e..b96facf15 100644 --- a/web/app/js/components/MetricsTable.jsx +++ b/web/app/js/components/MetricsTable.jsx @@ -1,5 +1,6 @@ import _ from 'lodash'; import BaseTable from './BaseTable.jsx'; +import ErrorModal from './ErrorModal.jsx'; import GrafanaLink from './GrafanaLink.jsx'; import { processedMetricsPropType } from './util/MetricUtils.js'; import PropTypes from 'prop-types'; @@ -60,12 +61,13 @@ const columnDefinitions = (resource, namespaces, onFilterClick, showNamespaceCol defaultSortOrder: 'ascend', sorter: (a, b) => (a.name || "").localeCompare(b.name), render: row => { + let nameContents; if (resource.toLowerCase() === "namespace") { - return {row.name}; + nameContents = {row.name}; } else if (!row.added) { - return row.name; + nameContents = row.name; } else { - return ( + nameContents = ( ); } + return ( + + {nameContents} + { _.isEmpty(row.errors) ? null : } + + ); } }, { diff --git a/web/app/js/components/ServiceMesh.jsx b/web/app/js/components/ServiceMesh.jsx index 96132867b..c97e6e06f 100644 --- a/web/app/js/components/ServiceMesh.jsx +++ b/web/app/js/components/ServiceMesh.jsx @@ -2,6 +2,7 @@ import _ from 'lodash'; import CallToAction from './CallToAction.jsx'; import ConduitSpinner from "./ConduitSpinner.jsx"; import ErrorBanner from './ErrorBanner.jsx'; +import ErrorModal from './ErrorModal.jsx'; import { incompleteMeshMessage } from './util/CopyUtils.jsx'; import Metric from './Metric.jsx'; import { numericSort } from './util/Utils.js'; @@ -41,11 +42,19 @@ const getClassification = (meshedPodCount, failedPodCount) => { const namespacesColumns = ConduitLink => [ { title: "Namespace", - dataIndex: "namespace", key: "namespace", defaultSortOrder: "ascend", sorter: (a, b) => (a.namespace || "").localeCompare(b.namespace), - render: d => {d} + render: d => { + return ( + + {d.namespace} + { _.isEmpty(d.errors) ? null : + + } + + ); + } }, { title: "Meshed pods", @@ -62,7 +71,8 @@ const namespacesColumns = ConduitLink => [ let containerWidth = 132; let percent = row.meshedPercent.get(); let barWidth = percent < 0 ? 0 : Math.round(percent * containerWidth); - let barType = getClassification(row.meshedPods, row.failedPods); + let barType = _.isEmpty(row.errors) ? + getClassification(row.meshedPods, row.failedPods) : "poor"; return ( { successRate: getSuccessRate(row), latency: getLatency(row), tlsRequestPercent: getTlsRequestPercentage(row), - added: row.meshedPodCount === row.runningPodCount + added: row.meshedPodCount === row.runningPodCount, + errors: row.errorsByPod }; }) .compact() diff --git a/web/app/test/MetricUtilsTest.js b/web/app/test/MetricUtilsTest.js index e6e860f75..494705d50 100644 --- a/web/app/test/MetricUtilsTest.js +++ b/web/app/test/MetricUtilsTest.js @@ -26,7 +26,8 @@ describe('MetricUtils', () => { P95: 2, P99: 7 }, - added: true + added: true, + errors: {} } ]; expect(result).to.have.length(1); diff --git a/web/app/test/fixtures/deployRollup.json b/web/app/test/fixtures/deployRollup.json index 52ce52c82..756b7cfd2 100644 --- a/web/app/test/fixtures/deployRollup.json +++ b/web/app/test/fixtures/deployRollup.json @@ -21,7 +21,8 @@ }, "timeWindow": "1m", "runningPodCount": "1", - "failedPodCount": null + "failedPodCount": null, + "errorsByPod": {} } ] }