Skip to content

Commit

Permalink
#146 added possibility to kill unstoppable scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
bugy committed Aug 20, 2019
1 parent 35c66c1 commit b573605
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 26 deletions.
9 changes: 9 additions & 0 deletions src/web/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,14 @@ def post(self, execution_id):
self.application.execution_service.stop_script(execution_id)


class ScriptKill(BaseRequestHandler):
@check_authorization
def post(self, execution_id):
validate_execution_id(execution_id, self)

self.application.execution_service.kill_script(execution_id)


class ScriptStreamSocket(tornado.websocket.WebSocketHandler):
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
Expand Down Expand Up @@ -796,6 +804,7 @@ def init(server_config: ServerConfig,
(r'/scripts/([^/]*)/([^/]*)/list-files', ScriptParameterListFiles),
(r'/executions/start', ScriptExecute),
(r'/executions/stop/(.*)', ScriptStop),
(r'/executions/kill/(.*)', ScriptKill),
(r'/executions/io/(.*)', ScriptStreamSocket),
(r'/executions/active', GetActiveExecutionIds),
(r'/executions/config/(.*)', GetExecutingScriptConfig),
Expand Down
50 changes: 42 additions & 8 deletions web-src/js/main-app/script-view.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
@click="executeScript">
Execute
</button>
<button class="button-stop btn red lighten-1"
<button class="button-stop btn"
:disabled="!enableStopButton"
v-bind:class="{ disabled: !enableStopButton}"
v-bind:class="{
disabled: !enableStopButton,
'red lighten-1': !killEnabled,
'grey darken-4': killEnabled}"
@click="stopScript">
Stop
{{stopButtonLabel}}
</button>
</div>
<LogPanel ref="logPanel" v-show="showLog && !hasErrors && !hideExecutionControls"/>
Expand Down Expand Up @@ -131,7 +134,25 @@
},
enableStopButton() {
return !isNull(this.currentExecutor) && this.currentExecutor.state.status === STATUS_EXECUTING;
return this.status === STATUS_EXECUTING;
},
stopButtonLabel() {
if (this.status === STATUS_EXECUTING) {
if (this.killEnabled) {
return 'Kill';
}
if (!isNull(this.killEnabledTimeout)) {
return 'Stop (' + this.killEnabledTimeout + ')';
}
}
return 'Stop';
},
status() {
return isNull(this.currentExecutor) ? null : this.currentExecutor.state.status;
},
showLog() {
Expand All @@ -147,7 +168,7 @@
},
inputPromptText() {
if (isNull(this.currentExecutor) || (this.currentExecutor.state.status !== STATUS_EXECUTING)) {
if (this.status !== STATUS_EXECUTING) {
return null;
}
Expand All @@ -160,6 +181,14 @@
}
return this.currentExecutor.state.logChunks;
},
killEnabled() {
return !isNull(this.currentExecutor) && this.currentExecutor.state.killEnabled;
},
killEnabledTimeout() {
return isNull(this.currentExecutor) ? null : this.currentExecutor.state.killTimeoutSec;
}
},
Expand Down Expand Up @@ -197,7 +226,11 @@
return;
}
this.$store.dispatch('executions/' + this.currentExecutor.state.id + '/stopExecution');
if (this.killEnabled) {
this.$store.dispatch('executions/' + this.currentExecutor.state.id + '/killExecution');
} else {
this.$store.dispatch('executions/' + this.currentExecutor.state.id + '/stopExecution');
}
},
sendUserInput(value) {
Expand Down Expand Up @@ -288,14 +321,15 @@
}
.button-execute {
flex: 6 1 auto;
flex: 6 1 5em;
margin-left: 2%;
margin-right: 0;
margin-top: 6px;
}
.button-stop {
flex: 1 0 auto;
flex: 1 0 5em;
margin-left: 12px;
margin-right: 2%;
Expand Down
94 changes: 76 additions & 18 deletions web-src/js/main-app/store/scriptExecutor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const STATUS_FINISHED = 'finished';
export const STATUS_DISCONNECTED = 'disconnected';
export const STATUS_ERROR = 'error';

let oneSecDelay = 1000;

export default (id, scriptName, parameterValues) => {

Expand All @@ -32,29 +33,66 @@ export default (id, scriptName, parameterValues) => {
downloadableFiles: [],
parameterValues: parameterValues,
status: STATUS_INITIALIZING,
scriptName: scriptName
scriptName: scriptName,

killIntervalId: null,
killTimeoutSec: null,
killEnabled: false
},

actions: {
reconnect({state, dispatch, commit}) {
commit('SET_STATUS', STATUS_EXECUTING);
attachToWebsocket(internalState, state, commit);
dispatch('setStatus', STATUS_EXECUTING);
attachToWebsocket(internalState, state, commit, dispatch);
},

stopExecution({state}) {
stopExecution({state, commit, dispatch}) {
if (isNull(state.killIntervalId) && (!state.killEnabled)) {
const intervalId = setInterval(() => {
dispatch('_tickKillInterval')
}, oneSecDelay);

commit('SET_KILL_INTERVAL', {id: intervalId, timeoutSec: 5, killEnabled: false});
}

return axios.post('executions/stop/' + state.id);
},

setInitialising({commit}) {
commit('SET_STATUS', STATUS_INITIALIZING);
_tickKillInterval({state, commit}) {

if (state.status !== STATUS_EXECUTING) {
if (!isNull(state.killIntervalId)) {
clearInterval(state.killIntervalId);
}
commit('SET_KILL_INTERVAL', {id: null, timeoutSec: null, killEnabled: false});
return;
}

if (state.killTimeoutSec <= 1) {
if (!isNull(state.killIntervalId)) {
clearInterval(state.killIntervalId);
}
commit('SET_KILL_INTERVAL', {id: null, timeoutSec: null, killEnabled: true});
return;
}

commit('DEC_KILL_INTERVAL');
},

killExecution({state}) {
return axios.post('executions/kill/' + state.id);
},

setInitialising({commit, dispatch}) {
commit('SET_LOG', 'Calling the script...');
dispatch('setStatus', STATUS_INITIALIZING);
},

start({state, commit}, executionId) {
start({state, dispatch, commit}, executionId) {
commit('SET_ID', executionId);
commit('SET_STATUS', STATUS_EXECUTING);
commit('SET_LOG', null);
attachToWebsocket(internalState, state, commit);
dispatch('setStatus', STATUS_EXECUTING);
attachToWebsocket(internalState, state, commit, dispatch);
},

sendUserInput({}, userInput) {
Expand All @@ -63,8 +101,8 @@ export default (id, scriptName, parameterValues) => {
}
},

setErrorStatus({commit}) {
commit('SET_STATUS', STATUS_ERROR);
setErrorStatus({dispatch}) {
dispatch('setStatus', STATUS_ERROR);
},

appendLog({commit}, log) {
Expand All @@ -80,6 +118,16 @@ export default (id, scriptName, parameterValues) => {
abort({dispatch}) {
return dispatch('stopExecution')
.finally(() => dispatch('cleanup'));
},

setStatus({commit, state}, status) {

if (!isNull(state.killIntervalId)) {
clearInterval(state.killIntervalId);
commit('SET_KILL_INTERVAL', {id: null, timeoutSec: null, killEnabled: false})
}

commit('SET_STATUS', status);
}
},

Expand Down Expand Up @@ -121,12 +169,22 @@ export default (id, scriptName, parameterValues) => {

SET_ID(state, id) {
state.id = id;
},

SET_KILL_INTERVAL(state, {id, timeoutSec, killEnabled}) {
state.killIntervalId = id;
state.killTimeoutSec = timeoutSec;
state.killEnabled = killEnabled;
},

DEC_KILL_INTERVAL(state) {
state.killTimeoutSec--;
}
}
}
}

function attachToWebsocket(internalState, state, commit) {
function attachToWebsocket(internalState, state, commit, dispatch) {
if (!isNull(internalState.websocket)) {
return;
}
Expand Down Expand Up @@ -164,21 +222,21 @@ function attachToWebsocket(internalState, state, commit) {
let executionFinished = (event.code === 1000);
if (!executionFinished) {
axios.get('executions/status/' + executionId)
.then(({status: data}) => {
if (data === 'finished') {
commit('SET_STATUS', STATUS_FINISHED);
.then(({data: status}) => {
if (status === 'finished') {
dispatch('setStatus', STATUS_FINISHED);
axios.post('executions/cleanup/' + executionId);
} else {
commit('SET_STATUS', STATUS_DISCONNECTED);
dispatch('setStatus', STATUS_DISCONNECTED);
}
})
.catch((error) => {
console.log('Failed to connect to the server: ' + error);
commit('SET_STATUS', STATUS_ERROR);
dispatch('setErrorStatus');
});

} else {
commit('SET_STATUS', STATUS_FINISHED);
dispatch('setStatus', STATUS_FINISHED);
axios.post('executions/cleanup/' + executionId);
}
});
Expand Down
2 changes: 2 additions & 0 deletions web-src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@stryker-mutator/mocha-framework": "^1.1.0",
"@stryker-mutator/webpack-transpiler": "^1.1.0",
"@vue/test-utils": "^1.0.0-beta.27",
"axios-mock-adapter": "^1.17.0",
"babel-loader": "^8.0.4",
"babel-plugin-rewire": "^1.2.0",
"chai": "^4.2.0",
Expand All @@ -39,6 +40,7 @@
"karma-webpack": "^3.0.5",
"mini-css-extract-plugin": "^0.5.0",
"mocha": "^5.2.0",
"mock-socket": "^9.0.0",
"sinon": "^7.4.1",
"style-loader": "^0.23.1",
"url-loader": "^2.1.0",
Expand Down
Loading

0 comments on commit b573605

Please sign in to comment.