Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Commit

Permalink
[0.9.0] Fix Load in progress message in performance dashboard (#2281)
Browse files Browse the repository at this point in the history
* Add LoadRun socket status
Remove await from runload fetch

Signed-off-by: markcor11 <[email protected]>

* return 409 if run in progress

Signed-off-by: markcor11 <[email protected]>

* Return error message in correct format

Signed-off-by: markcor11 <[email protected]>

* Return optional err

Signed-off-by: markcor11 <[email protected]>

* Clear status flag on error

Signed-off-by: markcor11 <[email protected]>
  • Loading branch information
markcor11 authored Feb 20, 2020
1 parent e3db42c commit a33528e
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 36 deletions.
58 changes: 39 additions & 19 deletions src/performance/dashboard/src/components/actions/ActionRunLoad.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
* IBM Corporation - initial API and implementation
******************************************************************************/

import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import IconRun from '@carbon/icons-react/es/play--filled/16'
import queryString from 'query-string';
import IconRun from '@carbon/icons-react/es/play--filled/16';
import IconStop from '@carbon/icons-react/lib/close--outline/16';
import { Button, InlineLoading } from 'carbon-components-react';
import { SocketEvents } from '../../utils/sockets/SocketEvents';
Expand Down Expand Up @@ -51,6 +52,11 @@ class ActionRunLoad extends React.Component {
componentDidMount() {
this.props.socket.on(SocketEvents.RUNLOAD_STATUS_CHANGED, data => {
if (data.projectID === this.props.projectID) {

if (queryString.parse(location.search).debugsocket) {
console.log("SocketIO RX: ", data);
}

switch (data.status) {
case 'preparing': {
this.setState({ showModalRunTest: false, loadRunStatus: data, inlineTextLabel: 'Preparing...' });
Expand All @@ -62,17 +68,32 @@ class ActionRunLoad extends React.Component {
}
case 'started': {
this.setState({ showModalRunTest: false, loadRunStatus: data, inlineTextLabel: 'Running...' });
this.startCountdown();
break;
}
case 'completed': {
// after receiving a loadrun completion message, wait a bit, then reset the button back to ready
this.setState({ showModalRunTest: false, loadRunStatus: data, inlineTextLabel: 'Completed...' });
let nextData = data;
nextData.status = 'idle';
setTimeout(() => this.setState({ loadRunStatus: nextData }), 3000);
break;
}
case 'cancelling': {
this.setState({ showModalRunTest: false, loadRunStatus: data, inlineTextLabel: 'Cancelling...' });
break;
}
case 'cancelled': {
this.setState({ showModalRunTest: false, loadRunStatus: data, inlineTextLabel: 'Cancelled...' });
let nextData = data;
nextData.status = 'idle';
setTimeout(() => this.setState({ loadRunStatus: nextData }), 2000);
setTimeout(() => this.setState({ loadRunStatus: nextData }), 2000);
break;
}
default: {
this.setState({ loadRunStatus: data });
if (queryString.parse(location.search).debugsocket) {
console.log("Ignoring UISocket RX: ",data);
}
}
}
}
Expand All @@ -83,13 +104,13 @@ class ActionRunLoad extends React.Component {
* Ask API to start a new test
*/
handleRunTestDlgStart(descriptionText) {
this.setState({ showModalRunTest: false, loadRunStatus: { status: 'requesting' }, inlineTextLabel: 'Requesting...' });
let instance = this;
this.requestRunLoad(descriptionText).then(function (result) {
instance.setState({ showModalRunTest: false, inlineTextLabel: 'Running...' });
switch (result.status) {
case 202: {
// success - request to start load accepted;
instance.startCountdown();
instance.setState({ loadRunStatus: { status: 'requested' }, inlineTextLabel: 'Requested...' });
break;
}
case 503: {
Expand All @@ -112,7 +133,7 @@ class ActionRunLoad extends React.Component {
/**
* Send a post to the metric/runload api to start a new load test.
* An optional description parameter can be provided.
* @param {string} desc
* @param {string} desc
*/
// eslint-disable-next-line class-methods-use-this
async requestRunLoad(desc) {
Expand All @@ -128,17 +149,16 @@ class ActionRunLoad extends React.Component {
}

async handleCancelLoad() {
this.setState({ inlineTextLabel: "Cancelling..." });
try {
const response = await fetch(`${AppConstants.API_SERVER}/api/v1/projects/${this.props.projectID}/loadtest/cancel`,
{
method: "POST",
headers: { "Content-Type": "application/json" }
});
const reply = await response;
this.setState({ inlineTextLabel: "Cancelled" });
console.error("Cancel accepted")
} catch (err) {
this.setState({ inlineTextLabel: "Cancel failed" });
console.error("Cancel failed:",err);
}
}

Expand All @@ -152,7 +172,7 @@ class ActionRunLoad extends React.Component {

showStartTestNotificationFail(err) {
this.setState(
{ showNotificationRunTestFail: true, notificationError: err },
{ showNotificationRunTestFail: true, notificationError: err, loadRunStatus: { status: '' } },
() => setTimeout(() => this.setState({ showNotificationRunTestFail: false }), 5000)
);
}
Expand All @@ -177,10 +197,10 @@ class ActionRunLoad extends React.Component {

render() {
const { showNotificationRunTestFail, loadRunStatus, inlineTextLabel, timeRemaining } = this.state;
let loadRunPreparing = loadRunStatus.status === 'preparing';
let loadRunStarting = loadRunStatus.status === 'starting';
let loadRunActive = loadRunStatus.status === 'started';
let loadRunSuccess = loadRunStatus.status === 'completed';

let loadRunCompleted = loadRunStatus.status === 'completed';
const options = ['preparing', 'starting', 'started', 'completed', 'requesting', 'requested', 'cancelling', 'cancelled']
const showBusy = options.includes(loadRunStatus.status)

let inlineTextLabelFormatted = (timeRemaining !== 0) ? `${inlineTextLabel}${timeRemaining}` : `${inlineTextLabel}`

Expand All @@ -189,10 +209,10 @@ class ActionRunLoad extends React.Component {
<RunTestNotificationFail notification={showNotificationRunTestFail} titleMessage={'Request Failed!'} notificationError={this.state.notificationError} />
<div className="ActionRunLoad">
{
(loadRunActive || loadRunSuccess || loadRunPreparing || loadRunStarting) ? (
(showBusy) ? (
<Fragment>
<div style={{ display: 'inline-block', verticalAlign: "middle" }}>
<InlineLoading style={{ marginLeft: '1rem' }} description={inlineTextLabelFormatted} success={loadRunSuccess} />
<InlineLoading style={{ marginLeft: '1rem' }} description={inlineTextLabelFormatted} success={loadRunCompleted} />
</div>
<div style={{ display: 'inline-block', verticalAlign: "middle", float: 'right' }}>
<Button onClick={() => this.handleCancelLoad()} style={{ verticalAlign: "middle", padding: 0, margin: 0 }} renderIcon={IconStop} kind="ghost" small iconDescription="Stop the load run"></Button>
Expand Down Expand Up @@ -228,7 +248,7 @@ const ActionRunLoadWithSocket = props => (
ActionRunLoad.propTypes = {
projectID: PropTypes.string.isRequired,
small: PropTypes.bool, // show small button
kind: PropTypes.string // button kind eg: 'ghost'
kind: PropTypes.string // button kind eg: 'ghost'
}


Expand Down
1 change: 1 addition & 0 deletions src/pfe/portal/modules/LoadRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ module.exports = class LoadRunner {
*/
this.socket.on('disconnect', () => {
log.info('Loadrunner has disconnected')
this.project.loadInProgress = false;
// If this.up is false we're already trying to reconnect
if (this.up) {
// socket.io-client will automatically reconnect and trigger the connect event.
Expand Down
11 changes: 3 additions & 8 deletions src/pfe/portal/modules/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,6 @@ module.exports = class User {
*/
async runLoad(project, description) {
log.debug("runLoad: project " + project.projectID + " loadInProgress=" + project.loadInProgress);
// If load in progress, throw an error
if (project.loadInProgress) {
throw new LoadRunError("RUN_IN_PROGRESS", `For project ${project.projectID}`);
}
project.loadInProgress = true;
try {
let config = await project.getLoadTestConfig();
Expand All @@ -176,12 +172,11 @@ module.exports = class User {
let url = projectProtocol + projectHost + ":" + projectPort + config.path;
config.url = url;
project.loadConfig = config;
log.info(`Running load for project: ${project.projectID} config: ${JSON.stringify(config)}`);
log.info(`Requesting load on project: ${project.projectID} config: ${JSON.stringify(config)}`);
const runLoadResp = await this.loadRunner.runLoad(config, project, description);
return runLoadResp;
} catch (err) {
// Reset run load flag and config in the project, and re-throw the error
project.loadInProgress = false;
project.loadConfig = null;
throw err;
}
Expand All @@ -192,15 +187,15 @@ module.exports = class User {
*/
async cancelLoad(project) {
log.debug("cancelLoad: project " + project.projectID + " loadInProgress=" + project.loadInProgress);

this.uiSocket.emit('runloadStatusChanged', { projectID: project.projectID, status: 'cancelling' });
if (project.loadInProgress) {
project.loadInProgress = false;
log.debug("Cancelling load for config: " + JSON.stringify(project.loadConfig));
this.uiSocket.emit('runloadStatusChanged', { projectID: project.projectID, status: 'cancelling' });
let cancelLoadResp = await this.loadRunner.cancelRunLoad(project.loadConfig);
this.uiSocket.emit('runloadStatusChanged', { projectID: project.projectID, status: 'cancelled' });
return cancelLoadResp;
}
this.uiSocket.emit('runloadStatusChanged', { projectID: project.projectID, status: 'cancelled' });
throw new LoadRunError("NO_RUN_IN_PROGRESS", `For project ${project.projectID}`);
}

Expand Down
21 changes: 12 additions & 9 deletions src/pfe/portal/routes/projects/loadtest.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const log = new Logger(__filename);
* 202 on success, 404 if project id does not exist, 400 if options are invalid or a run
* is already in progress, 500 if error
*/
router.post('/api/v1/projects/:id/loadtest', validateReq, async function(req,res){
router.post('/api/v1/projects/:id/loadtest', validateReq, function(req,res){
const user = req.cw_user;
const projectID = req.sanitizeParams('id');
let project = user.projectList.retrieveProject(projectID);
Expand All @@ -46,20 +46,23 @@ router.post('/api/v1/projects/:id/loadtest', validateReq, async function(req,res
}

try {
let runLoadResp = await user.runLoad(project, description);
// Response logic completed in ..docker/loadrunner/server.js
res.status(runLoadResp.statusCode).send(runLoadResp.body);
} catch(err) {
log.error(err);
if (err.code == LoadRunError.RUN_IN_PROGRESS) {
res.status(409).send(err.info);
log.info(`LoadTest route: loadInProgres = ${project.loadInProgress}`);
if (project.loadInProgress == undefined || !project.loadInProgress) {
user.runLoad(project, description);
res.status(202).send("");
} else {
res.status(500).send(err.info || err);
const err = new LoadRunError("RUN_IN_PROGRESS", `For project ${project.projectID}`);
res.status(409).send(err.info || err);
}
return;
} catch(err) {
log.error(err);
res.status(500).send(err.info || err);
}
}
});


/**
* API function to cancel load against a given project
* @param req, the request from the UI containing project id
Expand Down

0 comments on commit a33528e

Please sign in to comment.