Got rid of all NodeDetailsImageStatus code and replaced it with renderNodeDetailsExtras function.

This commit is contained in:
Filip Barl
2018-06-29 12:02:48 +02:00
parent 70d4d3f0ec
commit 7d85ed6a74
9 changed files with 8 additions and 214 deletions

View File

@@ -1,5 +1,4 @@
import debug from 'debug';
import { find } from 'lodash';
import { fromJS } from 'immutable';
import ActionTypes from '../constants/action-types';
@@ -827,30 +826,6 @@ export function shutdown() {
};
}
export function getImagesForService(orgId, serviceId) {
return (dispatch, getState, { api }) => {
dispatch({
type: ActionTypes.REQUEST_SERVICE_IMAGES,
serviceId
});
// Use the fluxv2 api
api.getFluxImages(orgId, serviceId)
.then((services) => {
dispatch({
type: ActionTypes.RECEIVE_SERVICE_IMAGES,
service: find(services, s => s.ID === serviceId),
serviceId
});
}, ({ errors }) => {
dispatch({
type: ActionTypes.RECEIVE_SERVICE_IMAGES,
errors
});
});
};
}
export function setMonitorState(monitor) {
return {
type: ActionTypes.MONITOR_STATE,

View File

@@ -201,7 +201,9 @@ class App extends React.Component {
{showingTroubleshootingMenu && <TroubleshootingMenu />}
{showingDetails && <Details />}
{showingDetails && <Details
renderNodeDetailsExtras={this.props.renderNodeDetailsExtras}
/>}
<div className="header">
{timeTravelSupported && this.props.renderTimeTravel()}
@@ -263,11 +265,13 @@ function mapStateToProps(state) {
App.propTypes = {
renderTimeTravel: PropTypes.func,
renderNodeDetailsExtras: PropTypes.func,
monitor: PropTypes.bool,
};
App.defaultProps = {
renderTimeTravel: () => <TimeTravelWrapper />,
renderNodeDetailsExtras: () => null,
monitor: false,
};

View File

@@ -60,6 +60,7 @@ class DetailsCard extends React.Component {
key={this.props.id}
nodeId={this.props.id}
mounted={this.state.mounted}
renderNodeDetailsExtras={this.props.renderNodeDetailsExtras}
{...this.props}
/>
</div>

View File

@@ -15,6 +15,7 @@ class Details extends React.Component {
index={index}
cardCount={details.size}
nodeControlStatus={controlStatus.get(obj.id)}
renderNodeDetailsExtras={this.props.renderNodeDetailsExtras}
{...obj}
/>
))}

View File

@@ -19,8 +19,6 @@ import NodeDetailsInfo from './node-details/node-details-info';
import NodeDetailsRelatives from './node-details/node-details-relatives';
import NodeDetailsTable from './node-details/node-details-table';
import Warning from './warning';
import CloudFeature from './cloud-feature';
import NodeDetailsImageStatus from './node-details/node-details-image-status';
const log = debug('scope:node-details');
@@ -249,14 +247,7 @@ class NodeDetails extends React.Component {
return null;
})}
<CloudFeature>
<NodeDetailsImageStatus
name={details.label}
metadata={details.metadata}
pseudo={details.pseudo}
topologyId={topologyId}
/>
</CloudFeature>
{this.props.renderNodeDetailsExtras({ topologyId, details })}
</div>
<Overlay faded={this.props.transitioning} />

View File

@@ -1,126 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import find from 'lodash/find';
import map from 'lodash/map';
import { CircularProgress } from 'weaveworks-ui-components';
import { getImagesForService } from '../../actions/app-actions';
const topologyWhitelist = ['kube-controllers'];
function newImagesAvailable(images, currentId) {
const current = find(images, i => i.ID === currentId);
if (current) {
const timestamp = new Date(current.CreatedAt);
return Boolean(find(images, i => new Date(i.CreatedAt) > timestamp));
}
return false;
}
class NodeDetailsImageStatus extends React.PureComponent {
constructor(props, context) {
super(props, context);
this.getImagesUrl = this.getImagesUrl.bind(this);
}
componentDidMount() {
if (this.shouldRender() && this.props.serviceId) {
this.props.getImagesForService(this.props.params.orgId, this.props.serviceId);
}
}
getImagesUrl() {
const { serviceId, params } = this.props;
return `/flux/${params.orgId}/services/${encodeURIComponent(serviceId)}`;
}
shouldRender() {
const { pseudo, topologyId } = this.props;
return !pseudo && topologyId && topologyWhitelist.includes(topologyId);
}
renderImages() {
const { errors, containers, isFetching } = this.props;
const error = !isFetching && errors;
if (isFetching) {
return (
<div className="progress-wrapper"><CircularProgress /></div>
);
}
if (error) {
return (
<p>Error: {JSON.stringify(map(errors, 'message'))}</p>
);
}
if (!containers) {
return 'No service images found';
}
return (
<div className="images">
{containers.map((container) => {
const statusText = newImagesAvailable(container.Available, container.Current.ID)
? <span className="new-image">New image(s) available</span>
: 'Image up to date';
return (
<div key={container.Name} className="wrapper">
<div className="node-details-table-node-label">{container.Name}</div>
<div className="node-details-table-node-value">{statusText}</div>
</div>
);
})}
</div>
);
}
render() {
const { containers } = this.props;
if (!this.shouldRender()) {
return null;
}
return (
<div className="node-details-content-section image-status">
<div className="node-details-content-section-header">
Container image status
{containers &&
<div>
<a
href={this.getImagesUrl()}
className="node-details-table-node-link">
View in Deploy
</a>
</div>
}
</div>
{this.renderImages()}
</div>
);
}
}
function mapStateToProps({ scope }, { metadata, name }) {
const namespace = find(metadata, d => d.id === 'kubernetes_namespace');
const nodeType = find(metadata, d => d.id === 'kubernetes_node_type');
const serviceId = (namespace && nodeType) ? `${namespace.value}:${nodeType.value.toLowerCase()}/${name}` : null;
const { containers, isFetching, errors } = scope.getIn(['serviceImages', serviceId]) || {};
return {
isFetching,
errors,
containers,
serviceId
};
}
export default connect(mapStateToProps, { getImagesForService })(NodeDetailsImageStatus);

View File

@@ -48,9 +48,7 @@ const ACTION_TYPES = [
'RECEIVE_NODES_FOR_TOPOLOGY',
'RECEIVE_NODES',
'RECEIVE_NOT_FOUND',
'RECEIVE_SERVICE_IMAGES',
'RECEIVE_TOPOLOGIES',
'REQUEST_SERVICE_IMAGES',
'RESET_LOCAL_VIEW_STATE',
'RESUME_TIME',
'ROUTE_TOPOLOGY',

View File

@@ -731,37 +731,4 @@ describe('RootReducer', () => {
constructEdgeId('def456', 'abc123')
]);
});
it('receives images for a service', () => {
const action = {
type: ActionTypes.RECEIVE_SERVICE_IMAGES,
serviceId: 'cortex/configs',
service: {
ID: 'cortex/configs',
Containers: [{
Available: [{
ID: 'quay.io/weaveworks/cortex-configs:master-1ca6274a',
CreatedAt: '2017-04-26T13:50:13.284736173Z'
}],
Current: { ID: 'quay.io/weaveworks/cortex-configs:master-1ca6274a' },
Name: 'configs'
}]
}
};
const nextState = reducer(initialState, action);
expect(nextState.getIn(['serviceImages', 'cortex/configs'])).toEqual({
isFetching: false,
errors: undefined,
containers: [{
Name: 'configs',
Current: {
ID: 'quay.io/weaveworks/cortex-configs:master-1ca6274a'
},
Available: [{
ID: 'quay.io/weaveworks/cortex-configs:master-1ca6274a',
CreatedAt: '2017-04-26T13:50:13.284736173Z'
}]
}]
});
});
});

View File

@@ -89,7 +89,6 @@ export const initialState = makeMap({
viewport: makeMap({ width: 0, height: 0 }),
websocketClosed: false,
zoomCache: makeMap(),
serviceImages: makeMap()
});
function calcSelectType(topology) {
@@ -749,22 +748,6 @@ export function rootReducer(state = initialState, action) {
return clearNodes(state);
}
case ActionTypes.REQUEST_SERVICE_IMAGES: {
return state.setIn(['serviceImages', action.serviceId], {
isFetching: true
});
}
case ActionTypes.RECEIVE_SERVICE_IMAGES: {
const { service, errors, serviceId } = action;
return state.setIn(['serviceImages', serviceId], {
isFetching: false,
containers: service ? service.Containers : null,
errors
});
}
case ActionTypes.MONITOR_STATE: {
return state.set('monitor', action.monitor);
}