Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@

class ScriptExecutorController extends Controller
{
public function index(Request $request)
public function index(Request $request, ScriptMicroserviceService $service)
{
if (!config('app.custom_executors')) {
abort(404);
}

$scriptMicroserviceEnabled = config('script-runner-microservice.enabled');

return view('admin.script-executors.index',
[
'script_microservice_enabled' => config('script-runner-microservice.enabled'),
'script_microservice_enabled' => $scriptMicroserviceEnabled,
'script_microservice_tenant_id' => $scriptMicroserviceEnabled
? $service->getInstanceUuid()
: null,
]);
}
}
39 changes: 37 additions & 2 deletions ProcessMaker/Http/Controllers/Api/ScriptExecutorController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Client\RequestException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use ProcessMaker\Enums\ScriptExecutorType;
Expand Down Expand Up @@ -124,7 +125,15 @@ public function store(Request $request, ScriptMicroserviceService $service)
ScriptExecutorCreated::dispatch($scriptExecutor->getAttributes());
BuildScriptExecutor::dispatch($scriptExecutor->id, $request->user()->id);
} else {
$service->createCustomExecutor($scriptExecutor);
try {
$service->createCustomExecutor($scriptExecutor);
} catch (RequestException $e) {
// The remote executor was rejected, so keeping the local record would leave
// an executor that can never be built.
$scriptExecutor->delete();

$this->throwMicroserviceError($e);
}
}

return ['status' => 'started', 'uuid' => $scriptExecutor->uuid, 'id' => $scriptExecutor->id];
Expand Down Expand Up @@ -189,7 +198,11 @@ public function update(Request $request, ScriptExecutor $scriptExecutor, ScriptM
);

if (config('script-runner-microservice.enabled') && $scriptExecutor->type == ScriptExecutorType::Custom) {
$service->updateCustomExecutor($scriptExecutor);
try {
$service->updateCustomExecutor($scriptExecutor);
} catch (RequestException $e) {
$this->throwMicroserviceError($e);
}
} else {
if (!empty($scriptExecutor->getChanges())) {
ScriptExecutorUpdated::dispatch($scriptExecutor->id, $original, $scriptExecutor->getChanges());
Expand Down Expand Up @@ -274,6 +287,28 @@ public function delete(Request $request, ScriptExecutor $scriptExecutor, ScriptM
return ['status' => 'done'];
}

/**
* Report a script microservice rejection on the form, since the build itself
* is only reported over the websocket channel.
*
* @param RequestException $e Failed script microservice response
*
* @return never
*
* @throws ValidationException|RequestException
*/
private function throwMicroserviceError(RequestException $e): never
{
if (!$e->response->clientError()) {
throw $e;
}

$detail = $e->response->json('detail');
$message = is_string($detail) && $detail !== '' ? $detail : $e->getMessage();

throw ValidationException::withMessages(['language' => [$message]]);
}

private function checkAuth($request)
{
if (!config('app.custom_executors')) {
Expand Down
125 changes: 90 additions & 35 deletions resources/js/admin/script-executors/ScriptExecutors.vue
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export default {
"filter",
"permission",
"script_microservice_enabled",
"script_microservice_tenant_id",
],
data() {
return {
Expand All @@ -271,7 +272,8 @@ export default {
exitCode: 0,
showDockerfile: false,
loading: true,
script_microservice_broadcast_uui: null,
activeBuildUuid: null,
pendingBuildEvents: [],

localLoadOnStart: true,
orderBy: "language",
Expand Down Expand Up @@ -356,16 +358,15 @@ export default {
}
);
}

if (this.script_microservice_enabled && this.script_microservice_tenant_id) {
this.subscribeToTenantBuildChannel();
}
},
watch: {
commandOutput() {
this.scrollToBottom();
},
script_microservice_broadcast_uui(newVal) {
if (newVal) {
this.subscribeToScriptMicroserviceChannel(newVal);
}
},
},
computed: {
modalTitle() {
Expand Down Expand Up @@ -434,7 +435,13 @@ export default {
},
setErrors(errors) {
this.status = "error";
this.errors = errors.response.data.errors;
this.errors = _.get(errors, "response.data.errors", {});
const messages = Object.values(this.errors)
.flat()
.filter((message) => typeof message === "string" && message !== "");
if (messages.length) {
this.output(messages.join("\n") + "\n");
}
},
doNotHideIfRunning(e) {
if (this.isRunning) {
Expand Down Expand Up @@ -472,13 +479,15 @@ export default {
save() {
this.resetProcessInfo();
this.status = "saving";
this.activeBuildUuid = this.formData.uuid || null;
this.pendingBuildEvents = [];
if (this.formData.id) {
const path = "/script-executors/" + this.formData.id;
ProcessMaker.apiClient
.put(path, this.formData)
.then((result) => {
this.status = _.get(result, "data.status", "error");
this.script_microservice_broadcast_uui = result.data.uuid;
this.setActiveBuildUuid(_.get(result, "data.uuid", this.formData.uuid));
})
.catch((e) => {
this.setErrors(e);
Expand All @@ -489,9 +498,10 @@ export default {
.post(path, this.formData)
.then((result) => {
this.status = _.get(result, "data.status", "error");
this.script_microservice_broadcast_uui = result.data.uuid;
this.setActiveBuildUuid(_.get(result, "data.uuid"));
if (this.status === "started") {
this.formData.id = result.data.id;
this.formData.uuid = result.data.uuid;
this.fetch(); // refresh the table (beneath the modal)
}
})
Expand All @@ -505,13 +515,17 @@ export default {
},
edit(row) {
this.formData = _.cloneDeep(row);
this.activeBuildUuid = row.uuid || null;
this.$refs.edit.show();
},
reset() {
this.formData = _.cloneDeep(this.emptyFormData);
this.errors = {};
this.showDockerfile = false;
(this.status = "idle"), this.resetProcessInfo();
this.status = "idle";
this.activeBuildUuid = null;
this.pendingBuildEvents = [];
this.resetProcessInfo();
},
resetProcessInfo() {
this.commandOutput = "";
Expand Down Expand Up @@ -550,31 +564,72 @@ export default {
onAddToBundle(data) {
this.$root.$emit('add-to-bundle', data);
},
subscribeToScriptMicroserviceChannel(name) {
const channel = `build-image-${name}`;
if (this.script_microservice_enabled) {
// Subscribe to new channel
window.ScriptMicroserviceEcho
.channel(channel)
.listenToAll((eventName, data) => {
this.status = this.status === "idle" ? "starting" : this.status;
switch (eventName) {
case ".build-image":
this.output(`${data}\n`);
break;
case ".build-finished":
this.pidFile = null;
this.exitCode = 0;
this.status = "done";
break;
case ".build-error":
this.output(data);
this.pidFile = null;
this.exitCode = 1;
this.status = "done";
break;
}
});
subscribeToTenantBuildChannel() {
if (!window.ScriptMicroserviceEcho) {
return;
}
const channel = `tenant-${this.script_microservice_tenant_id}-builds`;
window.ScriptMicroserviceEcho.channel(channel).listen(
".executor-build",
(data) => {
this.handleExecutorBuildEvent(data);
}
);
},
setActiveBuildUuid(uuid) {
if (!uuid) {
return;
}
this.activeBuildUuid = uuid;
const pending = this.pendingBuildEvents.filter(
(event) => event.executor_id === uuid
);
this.pendingBuildEvents = [];
pending.forEach((event) => this.applyExecutorBuildEvent(event));
},
handleExecutorBuildEvent(data) {
if (!data || typeof data !== "object") {
return;
}
if (!this.isRunning) {
return;
}
if (!this.activeBuildUuid) {
this.pendingBuildEvents.push(data);
return;
}
if (data.executor_id !== this.activeBuildUuid) {
return;
}
this.applyExecutorBuildEvent(data);
},
applyExecutorBuildEvent(data) {
if (this.status === "saving") {
this.status = "starting";
} else if (this.status === "idle") {
this.status = "starting";
}

if (data.type === "status") {
this.output(`[${data.phase}] ${data.message || ""}\n`);
} else if (data.message) {
const message = data.message.endsWith("\n")
? data.message
: `${data.message}\n`;
this.output(message);
}

if (data.phase === "completed" && data.type === "status") {
this.pidFile = null;
this.exitCode = 0;
this.status = "done";
return;
}

if (data.phase === "error" || data.type === "error") {
this.pidFile = null;
this.exitCode = 1;
this.status = "done";
}
},
},
Expand Down
1 change: 1 addition & 0 deletions resources/views/admin/script-executors/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<div class="card card-body">
<script-executors
:script_microservice_enabled="@json($script_microservice_enabled)"
script_microservice_tenant_id="{{$script_microservice_tenant_id}}"
></script-executors>
</div>
</div>
Expand Down
Loading