Canvas: Add worker kill action to monitoring tab
Change-Id: I8387dcf2320f9eccdb2e443fd4039e91ff35ed31
diff --git a/apps/canvas/back/src/index.ts b/apps/canvas/back/src/index.ts
index b33cbbc..cc8d9ef 100644
--- a/apps/canvas/back/src/index.ts
+++ b/apps/canvas/back/src/index.ts
@@ -902,6 +902,30 @@
}
};
+const handleQuitWorker: express.Handler = async (req, resp) => {
+ const projectId = Number(req.params["projectId"]);
+ const serviceName = req.params["serviceName"];
+ const workerId = req.params["workerId"];
+
+ const projectMonitor = projectMonitors.get(projectId);
+ if (!projectMonitor) {
+ resp.status(404).send({ error: "Project monitor not found" });
+ return;
+ }
+
+ try {
+ await projectMonitor.terminateWorker(serviceName, workerId);
+ resp.status(200).send({ message: "Worker termination initiated" });
+ } catch (error) {
+ console.error(
+ `Failed to terminate worker ${workerId} in service ${serviceName} for project ${projectId}:`,
+ error,
+ );
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
+ resp.status(500).send({ error: `Failed to terminate worker: ${errorMessage}` });
+ }
+};
+
const handleReloadWorker: express.Handler = async (req, resp) => {
const projectId = Number(req.params["projectId"]);
const serviceName = req.params["serviceName"];
@@ -1101,6 +1125,7 @@
projectRouter.post("/:projectId/anthropic-token", handleUpdateAnthropicToken);
projectRouter.get("/:projectId/env", handleEnv);
projectRouter.post("/:projectId/reload/:serviceName/:workerId", handleReloadWorker);
+ projectRouter.post("/:projectId/quitquitquit/:serviceName/:workerId", handleQuitWorker);
projectRouter.post("/:projectId/reload", handleReload);
projectRouter.get("/:projectId/logs/:service/:workerId", handleServiceLogs);
projectRouter.post("/:projectId/remove-deployment", handleRemoveDeployment);
diff --git a/apps/canvas/back/src/project_monitor.ts b/apps/canvas/back/src/project_monitor.ts
index df20e05..7dd2f3c 100644
--- a/apps/canvas/back/src/project_monitor.ts
+++ b/apps/canvas/back/src/project_monitor.ts
@@ -109,6 +109,23 @@
throw error; // Re-throw to be caught by ProjectMonitor
}
}
+
+ async terminateWorker(workerId: string): Promise<void> {
+ const workerAddress = this.workers.get(workerId);
+ if (!workerAddress) {
+ throw new Error(`Worker ${workerId} not found in service ${this.serviceName}`);
+ }
+ try {
+ const response = await fetch(`${workerAddress}/quitquitquit`, { method: "POST" });
+ if (!response.ok) {
+ throw new Error(`Failed to terminate worker ${workerId} at ${workerAddress}: ${response.statusText}`);
+ }
+ console.log(`Terminated worker ${workerId} in service ${this.serviceName}`);
+ } catch (error) {
+ console.error(`Error terminating worker ${workerId} in service ${this.serviceName}:`, error);
+ throw error; // Re-throw to be caught by ProjectMonitor
+ }
+ }
}
export class ProjectMonitor {
@@ -173,4 +190,12 @@
}
await serviceMonitor.reloadWorker(workerId);
}
+
+ async terminateWorker(serviceName: string, workerId: string): Promise<void> {
+ const serviceMonitor = this.serviceMonitors.get(serviceName);
+ if (!serviceMonitor) {
+ throw new Error(`Service ${serviceName} not found`);
+ }
+ await serviceMonitor.terminateWorker(workerId);
+ }
}