diff --git a/web/app/js/components/ResourceDetail.jsx b/web/app/js/components/ResourceDetail.jsx
index d9044162a..71ddc0905 100644
--- a/web/app/js/components/ResourceDetail.jsx
+++ b/web/app/js/components/ResourceDetail.jsx
@@ -1,14 +1,13 @@
import _ from 'lodash';
-import { apiErrorPropType } from './util/ApiHelpers.jsx';
import ErrorBanner from './ErrorBanner.jsx';
import MetricsTable from './MetricsTable.jsx';
import PageHeader from './PageHeader.jsx';
+import { processSingleResourceRollup } from './util/MetricUtils.js';
import PropTypes from 'prop-types';
import React from 'react';
-import { singularResource } from './util/Utils.js';
import { Spin } from 'antd';
-import withREST from './util/withREST.jsx';
-import { metricsPropType, processSingleResourceRollup } from './util/MetricUtils.js';
+import { withContext } from './util/AppContext.jsx';
+import { resourceTypeToCamelCase, singularResource } from './util/Utils.js';
import 'whatwg-fetch';
const getResourceFromUrl = (match, pathPrefix) => {
@@ -28,23 +27,19 @@ const getResourceFromUrl = (match, pathPrefix) => {
};
export class ResourceDetailBase extends React.Component {
- static defaultProps = {
- error: null
- }
-
static propTypes = {
api: PropTypes.shape({
PrefixedLink: PropTypes.func.isRequired,
}).isRequired,
- data: PropTypes.arrayOf(metricsPropType.isRequired).isRequired,
- error: apiErrorPropType,
- loading: PropTypes.bool.isRequired,
match: PropTypes.shape({}).isRequired,
pathPrefix: PropTypes.string.isRequired
}
constructor(props) {
super(props);
+ this.api = this.props.api;
+ this.handleApiError = this.handleApiError.bind(this);
+ this.loadFromServer = this.loadFromServer.bind(this);
this.state = this.getInitialState(props.match, props.pathPrefix);
}
@@ -53,44 +48,137 @@ export class ResourceDetailBase extends React.Component {
return {
namespace: resource.namespace,
resourceName: resource.name,
- resourceType: resource.type
+ resourceType: resource.type,
+ resource,
+ pollingInterval: 2000,
+ resourceMetrics: [],
+ podMetrics: [], // metrics for all pods whose owner is this resource
+ pendingRequests: false,
+ loaded: false,
+ error: null
};
}
- banner = () => {
- const {error} = this.props;
+ componentDidMount() {
+ this.loadFromServer();
+ this.timerId = window.setInterval(this.loadFromServer, this.state.pollingInterval);
+ }
- if (!error) {
+ componentWillReceiveProps(newProps) {
+ // React won't unmount this component when switching resource pages so we need to clear state
+ this.api.cancelCurrentRequests();
+ this.setState(this.getInitialState(newProps.match, newProps.pathPrefix));
+ }
+
+ componentWillUnmount() {
+ 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 { resource } = this.state;
+
+ this.api.setCurrentRequests([
+ // inbound stats for this resource
+ this.api.fetchMetrics(
+ `${this.api.urlsForResource(resource.type, resource.namespace)}&resource_name=${resource.name}`
+ ),
+ // list of all pods in this namespace (hack since we can't currently query for all pods in a resource)
+ this.api.fetchPods(resource.namespace),
+ // metrics for all pods in this namespace (hack, continued)
+ this.api.fetchMetrics(
+ `${this.api.urlsForResource("pod", resource.namespace)}`
+ ),
+ ]);
+
+ Promise.all(this.api.getCurrentPromises())
+ .then(([resourceRsp, podListRsp, podRsp]) => {
+ let resourceMetrics = processSingleResourceRollup(resourceRsp);
+ let podMetrics = processSingleResourceRollup(podRsp);
+
+ // INEFFICIENT: get metrics for all the pods belonging to this resource.
+ // Do this by querying for metrics for all pods in this namespace and then filtering
+ // out those pods whose owner is not this resource
+ // TODO: fix (#1467)
+ let podBelongsToResource = _.reduce(podListRsp.pods, (mem, pod) => {
+ if (_.get(pod, resourceTypeToCamelCase(resource.type)) === resource.namespace + "/" + resource.name) {
+ mem[pod.name] = true;
+ }
+
+ return mem;
+ }, {});
+
+ let podMetricsForResource = _.filter(podMetrics, pod => podBelongsToResource[pod.namespace + "/" + pod.name]);
+
+ this.setState({
+ resourceMetrics,
+ podMetrics: podMetricsForResource,
+ loaded: true,
+ pendingRequests: false,
+ error: null
+ });
+ })
+ .catch(this.handleApiError);
+ }
+
+ handleApiError = e => {
+ if (e.isCanceled) {
return;
}
- return