feature: Kill support in the UI (#297)

* feature: Kill command API (wip)

* feature: Kill support in the UI
This commit is contained in:
James Read 2024-04-27 22:51:45 +01:00 committed by GitHub
parent a783fc8cd4
commit dc6f6c2896
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 87 additions and 8 deletions

View File

@ -189,6 +189,17 @@ message EventExecutionFinished {
LogEntry log_entry = 1; LogEntry log_entry = 1;
} }
message KillActionRequest {
string execution_tracking_id = 1;
}
message KillActionResponse {
string execution_tracking_id = 1;
bool killed = 2;
bool already_completed = 3;
bool found = 4;
}
service OliveTinApiService { service OliveTinApiService {
rpc GetDashboardComponents(GetDashboardComponentsRequest) returns (GetDashboardComponentsResponse) { rpc GetDashboardComponents(GetDashboardComponentsRequest) returns (GetDashboardComponentsResponse) {
option (google.api.http) = { option (google.api.http) = {
@ -222,6 +233,13 @@ service OliveTinApiService {
}; };
} }
rpc KillAction(KillActionRequest) returns (KillActionResponse) {
option (google.api.http) = {
post: "/api/KillAction"
body: "*"
};
}
rpc ExecutionStatus(ExecutionStatusRequest) returns (ExecutionStatusResponse) { rpc ExecutionStatus(ExecutionStatusRequest) returns (ExecutionStatusResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/api/ExecutionStatus" post: "/api/ExecutionStatus"

View File

@ -74,6 +74,7 @@ type InternalLogEntry struct {
ExecutionStarted bool ExecutionStarted bool
ExecutionFinished bool ExecutionFinished bool
ExecutionTrackingID string ExecutionTrackingID string
Process *os.Process
/* /*
The following 3 properties are obviously on Action normally, but it's useful The following 3 properties are obviously on Action normally, but it's useful
@ -382,6 +383,8 @@ func stepExec(req *ExecutionRequest) bool {
runerr := cmd.Start() runerr := cmd.Start()
req.logEntry.Process = cmd.Process
waiterr := cmd.Wait() waiterr := cmd.Wait()
req.logEntry.ExitCode = int32(cmd.ProcessState.ExitCode()) req.logEntry.ExitCode = int32(cmd.ProcessState.ExitCode())

View File

@ -30,6 +30,28 @@ type oliveTinAPI struct {
executor *executor.Executor executor *executor.Executor
} }
func (api *oliveTinAPI) KillAction(ctx ctx.Context, req *pb.KillActionRequest) (*pb.KillActionResponse, error) {
ret := &pb.KillActionResponse{
ExecutionTrackingId: req.ExecutionTrackingId,
}
execReq, found := api.executor.Logs[req.ExecutionTrackingId]
ret.Found = found
if found {
err := execReq.Process.Kill()
if err == nil {
ret.AlreadyCompleted = true
} else {
ret.Killed = true
}
}
return ret, nil
}
func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest) (*pb.StartActionResponse, error) { func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest) (*pb.StartActionResponse, error) {
args := make(map[string]string) args := make(map[string]string)

View File

@ -125,11 +125,13 @@
</details> </details>
</div> </div>
<form method = "dialog"> <div class = "buttons">
<div class = "buttons"> <button name = "kill" title = "Kill" id = "execution-dialog-kill-action">Kill</button>
<form method = "dialog">
<button name = "Cancel" title = "Close">Close</button> <button name = "Cancel" title = "Close">Close</button>
</div> </form>
</form> </div>
</dialog> </dialog>
<template id = "tplArgumentForm"> <template id = "tplArgumentForm">

View File

@ -146,6 +146,7 @@ class ActionButton extends ExecutionFeedbackButton {
window.executionDialog.hideEverythingApartFromOutput() window.executionDialog.hideEverythingApartFromOutput()
} }
window.executionDialog.executionTrackingId = executionTrackingId
window.executionDialog.show(this) window.executionDialog.show(this)
} }

View File

@ -14,6 +14,8 @@ export class ExecutionDialog {
this.toggleSize() this.toggleSize()
} }
this.domBtnKill = document.getElementById('execution-dialog-kill-action')
this.domDatetimeStarted = document.getElementById('execution-dialog-datetime-started') this.domDatetimeStarted = document.getElementById('execution-dialog-datetime-started')
this.domDatetimeFinished = document.getElementById('execution-dialog-datetime-finished') this.domDatetimeFinished = document.getElementById('execution-dialog-datetime-finished')
this.domExitCode = document.getElementById('execution-dialog-exit-code') this.domExitCode = document.getElementById('execution-dialog-exit-code')
@ -36,6 +38,7 @@ export class ExecutionDialog {
reset () { reset () {
this.executionSeconds = 0 this.executionSeconds = 0
this.executionTrackingId = 'notset'
this.dlg.classList.remove('big') this.dlg.classList.remove('big')
this.dlg.style.maxWidth = 'calc(100vw - 2em)' this.dlg.style.maxWidth = 'calc(100vw - 2em)'
@ -54,6 +57,9 @@ export class ExecutionDialog {
this.domStdout.innerText = '' this.domStdout.innerText = ''
this.domStderr.innerText = '' this.domStderr.innerText = ''
this.domBtnKill.disabled = true
this.domBtnKill.onclick = () => {}
this.hideDetailsOnResult = false this.hideDetailsOnResult = false
this.domExecutionBasics.hidden = false this.domExecutionBasics.hidden = false
@ -68,6 +74,11 @@ export class ExecutionDialog {
this.domIcon.innerText = actionButton.domIcon.innerText this.domIcon.innerText = actionButton.domIcon.innerText
} }
this.domBtnKill.disabled = false
this.domBtnKill.onclick = () => {
this.killAction()
}
clearInterval(window.executionDialogTicker) clearInterval(window.executionDialogTicker)
this.executionSeconds = 0 this.executionSeconds = 0
this.executionTick() this.executionTick()
@ -82,6 +93,24 @@ export class ExecutionDialog {
this.dlg.showModal() this.dlg.showModal()
} }
killAction () {
const killActionArgs = {
executionTrackingId: this.executionTrackingId
}
window.fetch(window.restBaseUrl + 'KillAction', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(killActionArgs)
}).then((res) => {
console.log(res)
}).catch(err => {
throw err
})
}
executionTick () { executionTick () {
this.executionSeconds++ this.executionSeconds++
@ -93,8 +122,8 @@ export class ExecutionDialog {
this.domExecutionBasics.hidden = true this.domExecutionBasics.hidden = true
} }
fetchExecutionResult (uuid) { fetchExecutionResult (executionTrackingId) {
this.executionTrackingId = uuid this.executionTrackingId = executionTrackingId
const executionStatusArgs = { const executionStatusArgs = {
executionTrackingId: this.executionTrackingId executionTrackingId: this.executionTrackingId
@ -133,6 +162,8 @@ export class ExecutionDialog {
this.executionTrackingId = res.logEntry.executionTrackingId this.executionTrackingId = res.logEntry.executionTrackingId
this.domBtnKill.disabled = res.logEntry.executionFinished
if (res.logEntry.executionFinished) { if (res.logEntry.executionFinished) {
this.domStatus.innerText = 'Completed' this.domStatus.innerText = 'Completed'
this.domStatus.classList.add('action-success') this.domStatus.classList.add('action-success')

View File

@ -424,8 +424,10 @@ p.argument-wrapper {
display: flex; display: flex;
} }
form div.buttons { div.buttons {
text-align: right; display: flex;
justify-content: end;
gap: 1em;
} }
pre { pre {