Add a Create Service Profile dialog (#1933)

Add the ability to create and download a service profile from the web UI.

This form will be displayed in the call to action if no route metrics are found.
This commit is contained in:
Risha Mars 2018-12-05 15:08:10 -08:00 committed by GitHub
parent 7169eaef27
commit 442685674b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 3 deletions

View File

@ -1,24 +1,130 @@
import Button from '@material-ui/core/Button';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import PropTypes from 'prop-types';
import React from 'react';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import _ from 'lodash';
import { withContext } from './util/AppContext.jsx';
import { withStyles } from '@material-ui/core/styles';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
},
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
width: 200,
},
root: {
marginTop: theme.spacing.unit * 3,
marginBottom: theme.spacing.unit * 3,
},
});
class ConfigureProfilesMsg extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
query: {
service: "",
namespace: ""
}
};
}
handleClickOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
handleChange = name => {
let state = this.state;
return e => {
state.query[name] = e.target.value;
this.setState(state);
};
}
renderDownloadProfileForm = () => {
const { api, classes } = this.props;
let { query } = this.state;
let downloadUrl = api.prefixedUrl(`/profiles/new?service=${query.service}&namespace=${query.namespace}`);
return (
<React.Fragment>
<Button
className={classes.button}
variant="outlined"
color="primary"
onClick={this.handleClickOpen}>Create Service Profile
</Button>
<Dialog
open={this.state.open}
onClose={this.handleClose}
aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">New service profile</DialogTitle>
<DialogContent>
<DialogContentText>
To create a service profile, download a profile and then apply it with `kubectl apply`.
</DialogContentText>
<TextField
id="service"
label="Service"
className={classes.textField}
value={this.state.service}
onChange={this.handleChange('service')}
margin="normal" />
<TextField
id="namespace"
label="Namespace"
className={classes.textField}
value={this.state.name}
onChange={this.handleChange('namespace')}
margin="normal" />
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color="primary">Cancel</Button>
<a href={downloadUrl} style={{ textDecoration: 'none' }}>
<Button
disabled={_.isEmpty(query.service) || _.isEmpty(query.namespace)}
onClick={this.handleClose}
color="primary">Download
</Button>
</a>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
render() {
const { classes } = this.props;
return (
<Card className={classes.root}>
<CardContent>
<Typography>
No traffic found. Does the service have a service profile? You can create one with the `linkerd profile` command.
<Typography component="div">
No traffic found. Does the service have a service profile?
{this.renderDownloadProfileForm()}
</Typography>
</CardContent>
</Card>
@ -27,7 +133,10 @@ class ConfigureProfilesMsg extends React.Component {
}
ConfigureProfilesMsg.propTypes = {
api: PropTypes.shape({
prefixedFetch: PropTypes.func.isRequired,
}).isRequired,
classes: PropTypes.shape({}).isRequired
};
export default withStyles(styles, { withTheme: true })(ConfigureProfilesMsg);
export default withContext(withStyles(styles, { withTheme: true })(ConfigureProfilesMsg));

View File

@ -64,6 +64,7 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
"1h": "1 hour"
};
// for getting json api results
const apiFetch = path => {
if (!_.isEmpty(pathPrefix)) {
path = `${pathPrefix}${path}`;
@ -72,6 +73,15 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
return makeCancelable(fetch(path), r => r.json());
};
// for getting non-json results
const prefixedUrl = path => {
if (!_.isEmpty(pathPrefix)) {
path = `${pathPrefix}${path}`;
}
return path;
};
const fetchMetrics = path => {
if (path.indexOf("window") === -1) {
if (path.indexOf("?") === -1) {
@ -193,6 +203,7 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => {
return {
fetch: apiFetch,
prefixedUrl,
fetchMetrics,
fetchPods,
fetchServices,