mirror of https://github.com/linkerd/linkerd2.git
342 lines
9.0 KiB
JavaScript
342 lines
9.0 KiB
JavaScript
import Button from '@material-ui/core/Button';
|
|
import CheckIcon from '@material-ui/icons/Check';
|
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
|
import CloseIcon from '@material-ui/icons/Close';
|
|
import Dialog from '@material-ui/core/Dialog';
|
|
import DialogActions from '@material-ui/core/DialogActions';
|
|
import DialogContent from '@material-ui/core/DialogContent';
|
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
|
import ErrorBanner from './ErrorBanner.jsx';
|
|
import Grid from '@material-ui/core/Grid';
|
|
import Paper from '@material-ui/core/Paper';
|
|
import PriorityHigh from '@material-ui/icons/PriorityHigh';
|
|
import PropTypes from 'prop-types';
|
|
import React from 'react';
|
|
import SimpleChip from './util/Chip.jsx';
|
|
import Slide from '@material-ui/core/Slide';
|
|
import Typography from '@material-ui/core/Typography';
|
|
import withMobileDialog from '@material-ui/core/withMobileDialog';
|
|
import { withStyles } from '@material-ui/core/styles';
|
|
|
|
const styles = theme => ({
|
|
wrapper: {
|
|
marginBottom: '20px',
|
|
},
|
|
spinner: {
|
|
color: theme.status.dark.default,
|
|
position: 'absolute',
|
|
top: '50%',
|
|
left: '50%',
|
|
marginTop: '-40px',
|
|
marginLeft: '-40px',
|
|
},
|
|
dialog: {
|
|
minWidth: '60%',
|
|
},
|
|
dialogContent: {
|
|
overflow: 'hidden',
|
|
display: 'flex',
|
|
height: '860px',
|
|
},
|
|
contentWrapper: {
|
|
backgroundColor: 'black',
|
|
borderRadius: '5px',
|
|
maxHeight: '100%',
|
|
width: '100%',
|
|
overflowY: 'auto',
|
|
},
|
|
content: {
|
|
fontSize: '14px',
|
|
padding: theme.spacing(2),
|
|
color: 'white',
|
|
},
|
|
title: {
|
|
fontFamily: '\'Roboto Mono\', monospace',
|
|
textDecoration: 'underline',
|
|
color: 'white',
|
|
'&:not(:first-child)': {
|
|
paddingTop: '20px',
|
|
},
|
|
},
|
|
result: {
|
|
marginLeft: '10px',
|
|
fontFamily: '\'Roboto Mono\', monospace',
|
|
},
|
|
resultError: {
|
|
marginLeft: '20px',
|
|
fontFamily: '\'Roboto Mono\', monospace',
|
|
fontSize: '12px',
|
|
},
|
|
icon: {
|
|
marginLeft: '10px',
|
|
verticalAlign: 'bottom',
|
|
},
|
|
iconError: {
|
|
color: theme.status.dark.danger,
|
|
},
|
|
iconSuccess: {
|
|
color: theme.status.dark.good,
|
|
},
|
|
iconWarning: {
|
|
color: theme.status.dark.warning,
|
|
},
|
|
link: {
|
|
padding: '0px 5px',
|
|
color: 'white',
|
|
},
|
|
});
|
|
|
|
const Transition = React.forwardRef((props, ref) => {
|
|
return <Slide direction="down" ref={ref} {...props} />;
|
|
});
|
|
|
|
const Results = ({ title, results, classes }) => {
|
|
const getResultType = (error, warning) => {
|
|
if (error) {
|
|
if (warning) {
|
|
return 'warning';
|
|
} else {
|
|
return 'error';
|
|
}
|
|
} else {
|
|
return 'success';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Typography className={classes.title}>
|
|
{title}
|
|
</Typography>
|
|
|
|
{results.map((result, index) => {
|
|
const resultType = getResultType(result.Err, result.Warning);
|
|
|
|
return (
|
|
<Grid container direction="row" alignItems="flex-start" key={result.Description}>
|
|
<Grid item>
|
|
<Icon type={resultType} classes={classes} />
|
|
</Grid>
|
|
<Grid item xs>
|
|
<Typography className={classes.result} variant="body2" color="inherit" data-i18n={`${title}_${index + 1}`}>
|
|
{result.Description}
|
|
</Typography>
|
|
|
|
{resultType !== 'success' && (
|
|
<React.Fragment>
|
|
<Typography className={classes.resultError} variant="body2" color="inherit">
|
|
{result.ErrMsg}
|
|
</Typography>
|
|
|
|
<Typography className={classes.resultError} variant="body2" color="inherit" gutterBottom>
|
|
see
|
|
<a
|
|
className={classes.link}
|
|
href={result.HintURL}
|
|
target="_blank"
|
|
rel="noopener noreferrer">
|
|
{result.HintURL}
|
|
</a>
|
|
for hints
|
|
</Typography>
|
|
</React.Fragment>
|
|
)}
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
})}
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
Results.propTypes = {
|
|
results: PropTypes.arrayOf(PropTypes.shape({
|
|
Description: PropTypes.string.isRequired,
|
|
Err: PropTypes.any,
|
|
ErrMsg: PropTypes.string,
|
|
HintURL: PropTypes.string,
|
|
Warning: PropTypes.bool,
|
|
})).isRequired,
|
|
title: PropTypes.string.isRequired,
|
|
};
|
|
|
|
const Icon = ({ type, classes }) => {
|
|
return (
|
|
<React.Fragment>
|
|
{(() => {
|
|
switch (type) {
|
|
case 'success':
|
|
return <CheckIcon className={`${classes.icon} ${classes.iconSuccess}`} fontSize="small" />;
|
|
case 'error':
|
|
return <CloseIcon className={`${classes.icon} ${classes.iconError}`} fontSize="small" />;
|
|
case 'warning':
|
|
return <PriorityHigh className={`${classes.icon} ${classes.iconWarning}`} fontSize="small" />;
|
|
default:
|
|
return null;
|
|
}
|
|
})()}
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
Icon.propTypes = {
|
|
classes: PropTypes.shape({}).isRequired,
|
|
type: PropTypes.oneOf(['success', 'error', 'warning']).isRequired,
|
|
};
|
|
|
|
class CheckModal extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.api = props.api;
|
|
this.handleOpenChange = this.handleOpenChange.bind(this);
|
|
this.runCheck = this.runCheck.bind(this);
|
|
this.handleApiError = this.handleApiError.bind(this);
|
|
|
|
this.state = {
|
|
open: false,
|
|
running: false,
|
|
success: undefined,
|
|
results: {},
|
|
error: undefined,
|
|
};
|
|
}
|
|
|
|
handleOpenChange = () => {
|
|
this.setState(prevState => {
|
|
return {
|
|
open: !prevState.open,
|
|
};
|
|
});
|
|
}
|
|
|
|
runCheck = () => {
|
|
this.setState({
|
|
running: true,
|
|
open: true,
|
|
});
|
|
|
|
this.api.setCurrentRequests([this.api.fetchCheck()]);
|
|
this.serverPromise = Promise.all(this.api.getCurrentPromises())
|
|
.then(([response]) => {
|
|
this.setState({
|
|
running: false,
|
|
success: response.success,
|
|
results: response.results,
|
|
error: undefined,
|
|
});
|
|
})
|
|
.catch(this.handleApiError);
|
|
}
|
|
|
|
handleApiError = error => {
|
|
this.setState({
|
|
running: false,
|
|
error,
|
|
});
|
|
}
|
|
|
|
render() {
|
|
const { open, running, success, results, error } = this.state;
|
|
const { classes, fullScreen } = this.props;
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Grid
|
|
container
|
|
direction="row"
|
|
justify="center"
|
|
alignItems="center"
|
|
spacing={3}>
|
|
<Grid className={classes.wrapper} item>
|
|
<Button
|
|
variant="outlined"
|
|
color="primary"
|
|
disabled={running}
|
|
onClick={this.runCheck}>
|
|
Run Linkerd Check
|
|
</Button>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
{ error !== undefined && <ErrorBanner message={error} /> }
|
|
|
|
<Dialog
|
|
className={classes.dialog}
|
|
open={open}
|
|
scroll="paper"
|
|
TransitionComponent={Transition}
|
|
fullScreen={fullScreen}
|
|
maxWidth="md"
|
|
fullWidth
|
|
keepMounted
|
|
onClose={this.handleOpenChange}>
|
|
<DialogTitle>
|
|
<Grid
|
|
container
|
|
direction="row"
|
|
justify="space-between"
|
|
alignItems="center">
|
|
<Grid item>
|
|
Linkerd Check
|
|
</Grid>
|
|
|
|
{success !== undefined &&
|
|
<Grid item>
|
|
<SimpleChip
|
|
label={success ? 'Success' : 'Error'}
|
|
type={success ? 'good' : 'bad'} />
|
|
</Grid>
|
|
}
|
|
</Grid>
|
|
</DialogTitle>
|
|
|
|
<DialogContent className={classes.dialogContent}>
|
|
<Paper className={classes.contentWrapper} elevation={3}>
|
|
<div className={classes.content}>
|
|
{ running ? (
|
|
<CircularProgress size={80} className={classes.spinner} />
|
|
) : (
|
|
<React.Fragment>
|
|
{Object.keys(results).map(title => {
|
|
return (
|
|
<Results
|
|
key={title}
|
|
title={title}
|
|
results={results[title]}
|
|
classes={classes} />
|
|
);
|
|
})}
|
|
</React.Fragment>
|
|
)}
|
|
</div>
|
|
</Paper>
|
|
</DialogContent>
|
|
|
|
<DialogActions>
|
|
<Button onClick={this.runCheck} color="primary">
|
|
Re-Run Check
|
|
</Button>
|
|
|
|
<Button onClick={this.handleOpenChange} color="primary">
|
|
Close
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
}
|
|
|
|
CheckModal.propTypes = {
|
|
api: PropTypes.shape({
|
|
fetchCheck: PropTypes.func.isRequired,
|
|
getCurrentPromises: PropTypes.func.isRequired,
|
|
setCurrentRequests: PropTypes.func.isRequired,
|
|
}).isRequired,
|
|
fullScreen: PropTypes.bool.isRequired,
|
|
theme: PropTypes.shape({}).isRequired,
|
|
};
|
|
|
|
export default withMobileDialog()(withStyles(styles, { withTheme: true })(CheckModal));
|