linkerd2/web/app/js/components/Top.jsx

172 lines
4.3 KiB
JavaScript

import _ from 'lodash';
import { defaultMaxRps } from './util/TapUtils.jsx';
import ErrorBanner from './ErrorBanner.jsx';
import PageHeader from './PageHeader.jsx';
import PropTypes from 'prop-types';
import React from 'react';
import TapQueryCliCmd from './TapQueryCliCmd.jsx';
import TapQueryForm from './TapQueryForm.jsx';
import TopModule from './TopModule.jsx';
import { withContext } from './util/AppContext.jsx';
import './../../css/tap.css';
class Top extends React.Component {
static propTypes = {
api: PropTypes.shape({
PrefixedLink: PropTypes.func.isRequired,
}).isRequired,
pathPrefix: PropTypes.string.isRequired
}
constructor(props) {
super(props);
this.api = this.props.api;
this.loadFromServer = this.loadFromServer.bind(this);
this.state = {
error: null,
resourcesByNs: {},
authoritiesByNs: {},
query: {
resource: "",
namespace: "",
toResource: "",
toNamespace: "",
method: "",
path: "",
scheme: "",
authority: "",
maxRps: defaultMaxRps
},
pollingInterval: 10000,
tapRequestInProgress: false,
pendingRequests: false
};
}
componentDidMount() {
this.startServerPolling();
}
componentWillUnmount() {
this.stopServerPolling();
}
getResourcesByNs(rsp) {
let statTables = _.get(rsp, [0, "ok", "statTables"]);
let authoritiesByNs = {};
let resourcesByNs = _.reduce(statTables, (mem, table) => {
_.map(table.podGroup.rows, row => {
if (!mem[row.resource.namespace]) {
mem[row.resource.namespace] = [];
authoritiesByNs[row.resource.namespace] = [];
}
switch (row.resource.type.toLowerCase()) {
case "service":
break;
case "authority":
authoritiesByNs[row.resource.namespace].push(row.resource.name);
break;
default:
mem[row.resource.namespace].push(`${row.resource.type}/${row.resource.name}`);
}
});
return mem;
}, {});
return {
authoritiesByNs,
resourcesByNs
};
}
startServerPolling() {
this.loadFromServer();
this.timerId = window.setInterval(this.loadFromServer, this.state.pollingInterval);
}
stopServerPolling() {
window.clearInterval(this.timerId);
this.api.cancelCurrentRequests();
}
loadFromServer() {
if (this.state.pendingRequests) {
return; // don't make more requests if the ones we sent haven't completed
}
this.setState({
pendingRequests: true
});
let url = "/api/tps-reports?resource_type=all&all_namespaces=true";
this.api.setCurrentRequests([this.api.fetchMetrics(url)]);
this.serverPromise = Promise.all(this.api.getCurrentPromises())
.then(rsp => {
let { resourcesByNs, authoritiesByNs } = this.getResourcesByNs(rsp);
this.setState({
resourcesByNs,
authoritiesByNs,
pendingRequests: false
});
})
.catch(this.handleApiError);
}
handleApiError = e => {
if (e.isCanceled) {
return;
}
this.setState({
pendingRequests: false,
error: e
});
}
updateQuery = query => {
this.setState({
query
});
}
handleTapStart = () => {
this.setState({
tapRequestInProgress: true
});
}
handleTapStop = () => {
this.setState({
tapRequestInProgress: false
});
}
render() {
return (
<div>
{!this.state.error ? null :
<ErrorBanner message={this.state.error} onHideMessage={() => this.setState({ error: null })} />}
<PageHeader header="Top" />
<TapQueryForm
enableAdvancedForm={false}
handleTapStart={this.handleTapStart}
handleTapStop={this.handleTapStop}
resourcesByNs={this.state.resourcesByNs}
authoritiesByNs={this.state.authoritiesByNs}
tapRequestInProgress={this.state.tapRequestInProgress}
updateQuery={this.updateQuery}
query={this.state.query} />
<TapQueryCliCmd cmdName="top" query={this.state.query} />
<TopModule
pathPrefix={this.props.pathPrefix}
query={this.state.query}
startTap={this.state.tapRequestInProgress} />
</div>
);
}
}
export default withContext(Top);