import { displayName, friendlyTitle, metricToFormatter } from './util/Utils.js'; import BaseTable from './BaseTable.jsx'; import ErrorModal from './ErrorModal.jsx'; import GrafanaLink from './GrafanaLink.jsx'; import Grid from '@material-ui/core/Grid'; import JaegerLink from './JaegerLink.jsx'; import PropTypes from 'prop-types'; import React from 'react'; import SuccessRateMiniChart from './util/SuccessRateMiniChart.jsx'; import { Trans } from '@lingui/macro'; import _cloneDeep from 'lodash/cloneDeep'; import _each from 'lodash/each'; import _get from 'lodash/get'; import _isEmpty from 'lodash/isEmpty'; import { processedMetricsPropType } from './util/MetricUtils.jsx'; import { withContext } from './util/AppContext.jsx'; const tcpStatColumns = [ { title: columnTitleOpenConnections, dataIndex: 'tcp.openConnections', isNumeric: true, render: d => metricToFormatter.NO_UNIT(d.tcp.openConnections), sorter: d => d.tcp.openConnections, }, { title: columnTitleReadRate, dataIndex: 'tcp.readRate', isNumeric: true, render: d => metricToFormatter.BYTES(d.tcp.readRate), sorter: d => d.tcp.readRate, }, { title: columnTitleWriteRate, dataIndex: 'tcp.writeRate', isNumeric: true, render: d => metricToFormatter.BYTES(d.tcp.writeRate), sorter: d => d.tcp.writeRate, }, ]; const httpStatColumns = [ { title: columnTitleSuccessRate, dataIndex: 'successRate', isNumeric: true, render: d => , sorter: d => d.successRate, }, { title: columnTitleRPS, dataIndex: 'requestRate', isNumeric: true, render: d => metricToFormatter.NO_UNIT(d.requestRate), sorter: d => d.requestRate, }, { title: columnTitleP50Latency, dataIndex: 'P50', isNumeric: true, render: d => metricToFormatter.LATENCY(d.P50), sorter: d => d.P50, }, { title: columnTitleP95Latency, dataIndex: 'P95', isNumeric: true, render: d => metricToFormatter.LATENCY(d.P95), sorter: d => d.P95, }, { title: columnTitleP99Latency, dataIndex: 'P99', isNumeric: true, render: d => metricToFormatter.LATENCY(d.P99), sorter: d => d.P99, }, ]; const trafficSplitDetailColumns = [ { title: columnTitleApexService, dataIndex: 'apex', isNumeric: false, filter: d => !d.tsStats ? null : d.tsStats.apex, render: d => !d.tsStats ? null : d.tsStats.apex, sorter: d => !d.tsStats ? null : d.tsStats.apex, }, { title: columnTitleLeafService, dataIndex: 'leaf', isNumeric: false, filter: d => !d.tsStats ? null : d.tsStats.leaf, render: d => !d.tsStats ? null : d.tsStats.leaf, sorter: d => !d.tsStats ? null : d.tsStats.leaf, }, { title: columnTitleWeight, dataIndex: 'weight', isNumeric: true, filter: d => !d.tsStats ? null : d.tsStats.weight, render: d => !d.tsStats ? null : d.tsStats.weight, sorter: d => { if (!d.tsStats) { return -1; } if (parseInt(d.tsStats.weight, 10)) { return parseInt(d.tsStats.weight, 10); } else { return d.tsStats.weight; } }, }, ]; const gatewayColumns = [ { title: columnTitleClusterName, dataIndex: 'clusterName', isNumeric: false, render: d => !d.clusterName ? '---' : d.clusterName, sorter: d => !d.clusterName ? '---' : d.clusterName, }, { title: columnTitleAlive, dataIndex: 'alive', isNumeric: false, render: d => !d.alive ? 'FALSE' : 'TRUE', sorter: d => d.alive, }, { title: columnTitlePairedServices, dataIndex: 'pairedServices', isNumeric: false, render: d => d.pairedServices, sorter: d => d.pairedServices, }, { title: columnTitleP50Latency, dataIndex: 'P50', isNumeric: true, render: d => metricToFormatter.LATENCY(d.P50), sorter: d => d.P50, }, { title: columnTitleP95Latency, dataIndex: 'P95', isNumeric: true, render: d => metricToFormatter.LATENCY(d.P95), sorter: d => d.P95, }, { title: columnTitleP99Latency, dataIndex: 'P99', isNumeric: true, render: d => metricToFormatter.LATENCY(d.P99), sorter: d => d.P99, }, ]; const columnDefinitions = (resource, showNamespaceColumn, showNameColumn, PrefixedLink, isTcpTable, grafana, jaeger) => { const isAuthorityTable = resource === 'authority'; const isTrafficSplitTable = resource === 'trafficsplit'; const isMultiResourceTable = resource === 'multi_resource'; const isGatewayTable = resource === 'gateway'; const getResourceDisplayName = isMultiResourceTable ? displayName : d => d.name; const nsColumn = [ { title: columnTitleNamespace, dataIndex: 'namespace', filter: d => d.namespace, isNumeric: false, render: d => !d.namespace ? '---' : {d.namespace}, sorter: d => !d.namespace ? '---' : d.namespace, }, ]; const meshedColumn = { title: columnTitleMeshed, dataIndex: 'pods.totalPods', isNumeric: true, render: d => !d.pods ? null : `${d.pods.meshedPods}/${d.pods.totalPods}`, sorter: d => !d.pods ? -1 : d.pods.totalPods, }; const grafanaColumn = { title: columnTitleGrafana, key: 'grafanaDashboard', isNumeric: true, render: row => { if (!isAuthorityTable && (!row.added || _get(row, 'pods.totalPods') === '0')) { return null; } return ( ); }, }; const jaegerColumn = { title: columnTitleJaeger, key: 'JaegerDashboard', isNumeric: true, render: row => { if (!isAuthorityTable && (!row.added || _get(row, 'pods.totalPods') === '0')) { return null; } return ( ); }, }; const nameColumn = { title: isMultiResourceTable ? 'Resource' : friendlyTitle(resource).singular, dataIndex: 'name', isNumeric: false, filter: d => d.name, render: d => { let nameContents; if (resource === 'namespace') { nameContents = {d.name}; } else if (!d.added && (!isTrafficSplitTable || isAuthorityTable)) { nameContents = getResourceDisplayName(d); } else { nameContents = ( {getResourceDisplayName(d)} ); } return ( {nameContents} {_isEmpty(d.errors) ? null : } ); }, sorter: d => getResourceDisplayName(d) || -1, }; let columns = []; if (showNameColumn) { columns = [nameColumn]; } if (isTrafficSplitTable) { columns = columns.concat(trafficSplitDetailColumns); } if (isTcpTable) { columns = columns.concat(tcpStatColumns); } else if (isGatewayTable) { columns = columns.concat(gatewayColumns); } else { columns = columns.concat(httpStatColumns); } if (!isAuthorityTable && !isTrafficSplitTable && !isGatewayTable) { columns.splice(1, 0, meshedColumn); } if (!isTrafficSplitTable) { if (grafana !== '') { columns = columns.concat(grafanaColumn); } if (jaeger !== '') { columns = columns.concat(jaegerColumn); } } if (!showNamespaceColumn) { return columns; } else { return nsColumn.concat(columns); } }; const preprocessMetrics = metrics => { const tableData = _cloneDeep(metrics); _each(tableData, datum => { _each(datum.latency, (value, quantile) => { datum[quantile] = value; }); }); return tableData; }; const MetricsTable = ({ metrics, resource, showNamespaceColumn, showName, title, api, isTcpTable, selectedNamespace, grafana, jaeger }) => { const showNsColumn = resource === 'namespace' || selectedNamespace !== '_all' ? false : showNamespaceColumn; const showNameColumn = resource !== 'trafficsplit' ? true : showName; let orderBy = 'name'; if (resource === 'trafficsplit' && !showNameColumn) { orderBy = 'leaf'; } const columns = columnDefinitions(resource, showNsColumn, showNameColumn, api.PrefixedLink, isTcpTable, grafana, jaeger); const rows = preprocessMetrics(metrics); return ( ); }; MetricsTable.propTypes = { api: PropTypes.shape({ PrefixedLink: PropTypes.func.isRequired, }).isRequired, isTcpTable: PropTypes.bool, metrics: PropTypes.arrayOf(processedMetricsPropType), resource: PropTypes.string.isRequired, selectedNamespace: PropTypes.string.isRequired, showName: PropTypes.bool, showNamespaceColumn: PropTypes.bool, title: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), grafana: PropTypes.string, jaeger: PropTypes.string, }; MetricsTable.defaultProps = { showNamespaceColumn: true, showName: true, title: '', grafana: '', jaeger: '', isTcpTable: false, metrics: [], }; export default withContext(MetricsTable);