fix(code-queue): add workdirs contract checks
This commit is contained in:
@@ -395,6 +395,7 @@ function codeQueueK3sServiceIdForRequest(method: string, targetPath: string): st
|
||||
function codeQueueMasterControlPath(method: string, targetPath: string): boolean {
|
||||
const normalizedMethod = method.toUpperCase();
|
||||
if (targetPath === "/" || targetPath === "/health" || targetPath === "/live" || targetPath === "/logs") return true;
|
||||
if (targetPath === "/api/workdirs" || /^\/api\/workdirs\/[^/]+\/[^/]+\/.+$/u.test(targetPath)) return true;
|
||||
if (targetPath === "/api/queue-claim-move/self-test") return true;
|
||||
if (targetPath === "/api/queues" || targetPath === "/api/queues/merge") return true;
|
||||
if (/^\/api\/queues\/[^/]+(?:\/merge)?$/u.test(targetPath)) return true;
|
||||
@@ -404,6 +405,38 @@ function codeQueueMasterControlPath(method: string, targetPath: string): boolean
|
||||
return false;
|
||||
}
|
||||
|
||||
function codeQueueCompatWorkdirsResponse(service: MicroserviceConfig, status: number, contentType: string, bodyText: string): Response | null {
|
||||
if (status !== 404) return null;
|
||||
let body: any = null;
|
||||
try {
|
||||
body = JSON.parse(bodyText);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (body?.path !== "/api/workdirs") return null;
|
||||
const providerId = "D601";
|
||||
return jsonResponse({
|
||||
ok: true,
|
||||
workdirs: [{
|
||||
providerId,
|
||||
executionMode: "default",
|
||||
path: "/workspace",
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
source: "backend-core-compat",
|
||||
}],
|
||||
defaultProviderId: providerId,
|
||||
defaultWorkdir: "/workspace",
|
||||
remoteDefaultWorkdir: "/home/ubuntu",
|
||||
source: "backend-core-compat-code-queue-workdirs",
|
||||
compat: {
|
||||
reason: "code-queue-mgr version does not yet expose /api/workdirs",
|
||||
upstreamStatus: status,
|
||||
upstreamContentType: contentType,
|
||||
},
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cache helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1026,7 +1059,24 @@ async function fetchMicroserviceUpstreamResponse(
|
||||
): Promise<Response> {
|
||||
if (service.id === "code-queue" && codeQueueMasterControlPath(method, targetPath)) {
|
||||
const mgr = microserviceById("code-queue-mgr");
|
||||
if (mgr !== null) return directMicroserviceResponse(mgr, method, targetPath, proxyOptions, requestHeaders, bodyText, abortSignal);
|
||||
if (mgr !== null) {
|
||||
const response = await directMicroserviceResponse(mgr, method, targetPath, proxyOptions, requestHeaders, bodyText, abortSignal);
|
||||
if (method.toUpperCase() === "GET" && targetPath === "/api/workdirs") {
|
||||
const contentType = response.headers.get("content-type") ?? "text/plain; charset=utf-8";
|
||||
const responseBody = await response.text();
|
||||
const compat = codeQueueCompatWorkdirsResponse(service, response.status, contentType, responseBody);
|
||||
if (compat !== null) return compat;
|
||||
return new Response(responseBody, {
|
||||
status: response.status,
|
||||
headers: {
|
||||
"content-type": contentType,
|
||||
"x-unidesk-proxy-mode": response.headers.get("x-unidesk-proxy-mode") ?? "direct",
|
||||
"x-unidesk-response-truncated": response.headers.get("x-unidesk-response-truncated") ?? "false",
|
||||
},
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
logger("warn", "code_queue_mgr_missing_fallback_to_d601", { method, targetPath });
|
||||
}
|
||||
if (isK3sctlManagedMicroservice(service)) {
|
||||
|
||||
@@ -154,6 +154,47 @@ fn code_queue_scheduler_only_path(method: &Method, target_path: &str) -> bool {
|
||||
&& !(target_path == "/" || target_path == "/health")
|
||||
}
|
||||
|
||||
fn code_queue_fallback_workdirs_response(
|
||||
service: &MicroserviceConfig,
|
||||
status: u16,
|
||||
content_type: &str,
|
||||
body_text: &str,
|
||||
) -> Option<Response> {
|
||||
if status != 404 {
|
||||
return None;
|
||||
}
|
||||
let body = serde_json::from_str::<Value>(body_text).ok()?;
|
||||
if body.get("path").and_then(Value::as_str) != Some("/api/workdirs") {
|
||||
return None;
|
||||
}
|
||||
let provider_id = service.provider_id.clone();
|
||||
let fallback = json!({
|
||||
"ok": true,
|
||||
"workdirs": [{
|
||||
"providerId": provider_id,
|
||||
"executionMode": "default",
|
||||
"path": "/workspace",
|
||||
"createdAt": null,
|
||||
"updatedAt": null,
|
||||
"source": "backend-core-compat"
|
||||
}],
|
||||
"defaultProviderId": provider_id,
|
||||
"defaultWorkdir": "/workspace",
|
||||
"remoteDefaultWorkdir": "/home/ubuntu",
|
||||
"source": "backend-core-compat-code-queue-workdirs",
|
||||
"compat": {
|
||||
"reason": "code-queue-mgr version does not yet expose /api/workdirs",
|
||||
"upstreamStatus": status,
|
||||
"upstreamContentType": content_type
|
||||
}
|
||||
});
|
||||
Some(response_with_body(
|
||||
200,
|
||||
"application/json; charset=utf-8",
|
||||
Body::from(serde_json::to_string(&fallback).unwrap_or_else(|_| "{\"ok\":true,\"workdirs\":[]}".to_string())),
|
||||
))
|
||||
}
|
||||
|
||||
fn path_matches_task_detail(path: &str) -> bool {
|
||||
path.strip_prefix("/api/tasks/")
|
||||
.is_some_and(|rest| !rest.is_empty() && !rest.contains('/'))
|
||||
@@ -926,7 +967,7 @@ async fn fetch_microservice_upstream_response(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
return direct_microservice_response(
|
||||
let response = direct_microservice_response(
|
||||
&direct_code_queue_mgr_service(service, None),
|
||||
method,
|
||||
target_path,
|
||||
@@ -935,6 +976,18 @@ async fn fetch_microservice_upstream_response(
|
||||
body_text,
|
||||
)
|
||||
.await;
|
||||
if method == Method::GET && target_path == "/api/workdirs" {
|
||||
let (status, content_type, response_body) = response_to_text(response).await.unwrap_or((
|
||||
502,
|
||||
"text/plain".to_string(),
|
||||
"failed to read code-queue workdirs response".to_string(),
|
||||
));
|
||||
if let Some(fallback) = code_queue_fallback_workdirs_response(service, status, &content_type, &response_body) {
|
||||
return fallback;
|
||||
}
|
||||
return response_with_body(status, &content_type, Body::from(response_body));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
if is_k3sctl_managed_microservice(service) {
|
||||
return k3sctl_adapter_microservice_response(
|
||||
|
||||
Reference in New Issue
Block a user