Surface TCP stats in more places in the dashboard (#2464)

Show the TCP stats table in Namespace Overview page (the landing page) 
as well as the Namespace pages.
This commit is contained in:
Risha Mars 2019-03-07 14:56:04 -04:00 committed by GitHub
parent f258cf0d3a
commit f2be6cb058
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 27 deletions

View File

@ -6,7 +6,6 @@ import NetworkGraph from './NetworkGraph.jsx';
import PropTypes from 'prop-types';
import React from 'react';
import Spinner from './util/Spinner.jsx';
import Typography from '@material-ui/core/Typography';
import _filter from 'lodash/filter';
import _get from 'lodash/get';
import _isEmpty from 'lodash/isEmpty';
@ -84,7 +83,7 @@ class Namespaces extends React.Component {
}
this.setState({ pendingRequests: true });
this.api.setCurrentRequests([this.api.fetchMetrics(this.api.urlsForResource("all", this.state.ns))]);
this.api.setCurrentRequests([this.api.fetchMetrics(this.api.urlsForResource("all", this.state.ns, true))]);
Promise.all(this.api.getCurrentPromises())
.then(([allRollup]) => {
@ -117,8 +116,8 @@ class Namespaces extends React.Component {
}
return (
<div className="page-section">
<Typography variant="h5">{friendlyTitle(resource).plural}</Typography>
<MetricsTable
title={friendlyTitle(resource).plural}
resource={resource}
metrics={metrics}
showNamespaceColumn={false} />
@ -127,16 +126,16 @@ class Namespaces extends React.Component {
}
render() {
const {metrics} = this.state;
const { metrics } = this.state;
let noMetrics = _isEmpty(metrics.pod);
let deploymentsWithMetrics = _filter(metrics.deployment, d => d.requestRate > 0);
return (
<div className="page-content">
{ !this.state.error ? null : <ErrorBanner message={this.state.error} /> }
{ !this.state.loaded ? <Spinner /> : (
{!this.state.error ? null : <ErrorBanner message={this.state.error} />}
{!this.state.loaded ? <Spinner /> : (
<div>
{ noMetrics ? <div>No resources detected.</div> : null}
{noMetrics ? <div>No resources detected.</div> : null}
{
_isEmpty(deploymentsWithMetrics) ? null :
<NetworkGraph namespace={this.state.ns} deployments={metrics.deployment} />
@ -148,6 +147,17 @@ class Namespaces extends React.Component {
{this.renderResourceSection("statefulset", metrics.statefulset)}
{this.renderResourceSection("job", metrics.job)}
{this.renderResourceSection("authority", metrics.authority)}
{
noMetrics ? null :
<div className="page-section">
<MetricsTable
title="TCP"
resource="pod"
metrics={metrics.pod}
isTcpTable={true} />
</div>
}
</div>
)}
</div>);

View File

@ -79,7 +79,9 @@ class NamespaceLanding extends React.Component {
this.api.fetchMetrics(this.api.urlsForResource("namespace"))
];
if (!_isEmpty(this.state.selectedNs)) {
apiRequests = apiRequests.concat([this.api.fetchMetrics(this.api.urlsForResource("all", this.state.selectedNs))]);
apiRequests = apiRequests.concat([
this.api.fetchMetrics(this.api.urlsForResource("all", this.state.selectedNs, true))
]);
}
this.api.setCurrentRequests(apiRequests);
@ -149,7 +151,7 @@ class NamespaceLanding extends React.Component {
<Grid container direction="column" spacing={16}>
<Grid item><Typography variant="h4">Namespace: {namespace}</Typography></Grid>
<Grid item><Divider /></Grid>
<Grid item>{ noMetrics ? <div>No resources detected.</div> : null}</Grid>
<Grid item>{noMetrics ? <div>No resources detected.</div> : null}</Grid>
{this.renderResourceSection("deployment", metrics.deployment)}
{this.renderResourceSection("daemonset", metrics.daemonset)}
@ -158,6 +160,19 @@ class NamespaceLanding extends React.Component {
{this.renderResourceSection("statefulset", metrics.statefulset)}
{this.renderResourceSection("job", metrics.job)}
{this.renderResourceSection("authority", metrics.authority)}
{
noMetrics ? null :
<Grid container direction="column" justify="center">
<Grid item>
<MetricsTable
title="TCP"
resource="pod"
metrics={metrics.pod}
isTcpTable={true} />
</Grid>
</Grid>
}
</Grid>
);
}
@ -167,7 +182,7 @@ class NamespaceLanding extends React.Component {
let hr = (
<Grid container justify="space-between" alignItems="center">
<Grid item><Typography variant="subtitle1">{ns.name}</Typography></Grid>
{!ns.added ? null : <Grid item><SimpleChip label="meshed" type="good" /></Grid> }
{!ns.added ? null : <Grid item><SimpleChip label="meshed" type="good" /></Grid>}
</Grid>
);
return {
@ -192,8 +207,8 @@ class NamespaceLanding extends React.Component {
render() {
return (
<div className="page-content">
{ !this.state.error ? null : <ErrorBanner message={this.state.error} /> }
{ !this.state.loaded ? <Spinner /> : this.renderAccordion() }
{!this.state.error ? null : <ErrorBanner message={this.state.error} />}
{!this.state.loaded ? <Spinner /> : this.renderAccordion()}
</div>);
}
}

View File

@ -105,7 +105,7 @@ export class ResourceDetailBase extends React.Component {
// rcs, deploys, replicasets, etc but not pods or authorities
let shouldExclude = this.state.resourceType === "pod" ?
r => r !== "pod" :
r => r === "pod" || r === "authority" || r === "service";
r => r === "pod" || r === "authority" || r === "service";
return _reduce(metricsByResource, (mem, resourceMetrics, resource) => {
if (shouldExclude(resource)) {
return mem;
@ -131,7 +131,7 @@ export class ResourceDetailBase extends React.Component {
this.api.fetchPods(resource.namespace),
// metrics for all pods in this namespace (hack, continued)
this.api.fetchMetrics(
`${this.api.urlsForResource("pod", resource.namespace)}&tcp_stats=true`
`${this.api.urlsForResource("pod", resource.namespace, true)}`
),
// upstream resources of this resource (meshed traffic only)
this.api.fetchMetrics(
@ -269,9 +269,9 @@ export class ResourceDetailBase extends React.Component {
<Grid item><Typography variant="h5">{resourceType}/{resourceName}</Typography></Grid>
<Grid item>
<Grid container spacing={8}>
{ showNoTrafficMsg ? <Grid item><SimpleChip label="no traffic" type="warning" /></Grid> : null }
{showNoTrafficMsg ? <Grid item><SimpleChip label="no traffic" type="warning" /></Grid> : null}
<Grid item>
{ resourceIsMeshed ?
{resourceIsMeshed ?
<SimpleChip label="meshed" type="good" /> :
<SimpleChip label="unmeshed" type="bad" />
}
@ -301,24 +301,24 @@ export class ResourceDetailBase extends React.Component {
updateUnmeshedSources={this.updateUnmeshedSources}
disableTop={!resourceIsMeshed} />
{ _isEmpty(upstreams) ? null : (
{_isEmpty(upstreams) ? null : (
<React.Fragment>
<MetricsTable
resource="multi_resource"
title="Inbound"
metrics={upstreamMetrics} />
</React.Fragment>
)
)
}
{ _isEmpty(this.state.downstreamMetrics) ? null : (
{_isEmpty(this.state.downstreamMetrics) ? null : (
<React.Fragment>
<MetricsTable
resource="multi_resource"
title="Outbound"
metrics={downstreamMetrics} />
</React.Fragment>
)
)
}
{

View File

@ -17,7 +17,7 @@ const checkFetchOk = resp => {
throw {
status: resp.status,
url: resp.url,
statusText:resp.statusText,
statusText: resp.statusText,
error: error.error
};
});
@ -118,10 +118,20 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
metricsWindow = window;
};
const urlsForResource = (type, namespace) => {
const urlsForResource = (type, namespace, includeTcp) => {
// Traffic Performance Summary. This retrieves stats for the given resource.
let baseUrl = '/api/tps-reports?resource_type=' + type;
return !namespace ? baseUrl + '&all_namespaces=true' : baseUrl + '&namespace=' + namespace;
let resourceUrl = '/api/tps-reports?resource_type=' + type;
if (_isEmpty(namespace)) {
resourceUrl += '&all_namespaces=true';
} else {
resourceUrl += '&namespace=' + namespace;
}
if (includeTcp) {
resourceUrl += '&tcp_stats=true';
}
return resourceUrl;
};
// maintain a list of a component's requests,
@ -157,7 +167,7 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
return (
<Link
to={url}
{...(this.props.targetBlank ? {target:'_blank'} : {})}>
{...(this.props.targetBlank ? { target: '_blank' } : {})}>
{this.props.children}
</Link>
);
@ -175,10 +185,10 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
};
// a prefixed link to a Resource Detail page
const ResourceLink = ({resource, linkText}) => {
const ResourceLink = ({ resource, linkText }) => {
return (
<PrefixedLink to={generateResourceURL(resource)}>
{ linkText || resource.type + "/" + resource.name}
{linkText || resource.type + "/" + resource.name}
</PrefixedLink>
);
};

View File

@ -286,5 +286,11 @@ describe('ApiHelpers', () => {
let deploymentUrls = api.urlsForResource("pod", "my-ns");
expect(deploymentUrls).toEqual('/api/tps-reports?resource_type=pod&namespace=my-ns');
});
it('queries for TCP stats when specified', () => {
api = ApiHelpers();
let url = api.urlsForResource('sts', '', true);
expect(url).toEqual('/api/tps-reports?resource_type=sts&all_namespaces=true&tcp_stats=true');
})
});
});