fix: preserve adopted resource specs
This commit is contained in:
+99
-12
@@ -64,7 +64,7 @@ import {
|
||||
type Workspace,
|
||||
type WorkspaceStore,
|
||||
} from "./workspace-store";
|
||||
import { REDACTED, redactString } from "./redact";
|
||||
import { redactString } from "./redact";
|
||||
|
||||
const API_PREFIX = "/api/v2";
|
||||
const RUNTIME_SESSION_MS = 24 * 60 * 60 * 1000;
|
||||
@@ -121,6 +121,7 @@ export type UnknownFailureLog = {
|
||||
code: string;
|
||||
errorName: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
kubernetesStatus?: { status?: string; reason?: string; code?: number };
|
||||
};
|
||||
|
||||
@@ -334,8 +335,10 @@ export function createApp(
|
||||
? error.body
|
||||
: undefined;
|
||||
if (!candidate) return;
|
||||
const status = typeof candidate.status === "string" ? REDACTED : undefined;
|
||||
const reason = typeof candidate.reason === "string" ? REDACTED : undefined;
|
||||
const status =
|
||||
typeof candidate.status === "string" ? candidate.status : undefined;
|
||||
const reason =
|
||||
typeof candidate.reason === "string" ? candidate.reason : undefined;
|
||||
const code =
|
||||
typeof candidate.code === "number" ? candidate.code : undefined;
|
||||
return status || reason || code !== undefined
|
||||
@@ -343,6 +346,51 @@ export function createApp(
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function kubernetesError(
|
||||
error: unknown,
|
||||
): { status: number; detail: string } | undefined {
|
||||
if (!isRecord(error) || typeof error.statusCode !== "number") return;
|
||||
const status = error.statusCode;
|
||||
if (!Number.isInteger(status) || status < 100 || status > 599) return;
|
||||
const body = isRecord(error.body) ? error.body : undefined;
|
||||
if (
|
||||
!body ||
|
||||
body.kind !== "Status" ||
|
||||
body.apiVersion !== "v1" ||
|
||||
body.status !== "Failure" ||
|
||||
typeof body.reason !== "string" ||
|
||||
typeof body.message !== "string" ||
|
||||
!Number.isInteger(body.code) ||
|
||||
body.code !== status
|
||||
)
|
||||
return;
|
||||
return { status, detail: body.message };
|
||||
}
|
||||
|
||||
function errorDiagnostics(error: unknown): {
|
||||
errorName: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
} {
|
||||
if (error instanceof Error)
|
||||
return {
|
||||
errorName: error.name,
|
||||
message: error.message,
|
||||
...(error.stack && { stack: error.stack }),
|
||||
};
|
||||
if (isRecord(error))
|
||||
return {
|
||||
errorName: typeof error.name === "string" ? error.name : "UnknownError",
|
||||
message:
|
||||
typeof error.message === "string" ? error.message : "Unknown error",
|
||||
...(typeof error.stack === "string" && { stack: error.stack }),
|
||||
};
|
||||
return {
|
||||
errorName: "UnknownError",
|
||||
message: typeof error === "string" ? error : "Unknown error",
|
||||
};
|
||||
}
|
||||
|
||||
function logRequestError(
|
||||
request: Request,
|
||||
error: unknown,
|
||||
@@ -358,6 +406,9 @@ export function createApp(
|
||||
},
|
||||
): void {
|
||||
const pathname = new URL(request.url).pathname;
|
||||
const diagnostics = errorDiagnostics(error);
|
||||
const unknownFailure =
|
||||
event === "request.failed" && code === "INTERNAL_ERROR";
|
||||
logger.error({
|
||||
event,
|
||||
requestId: requestIds.get(request) ?? makeRequestId(),
|
||||
@@ -367,11 +418,13 @@ export function createApp(
|
||||
...(operationId && { operationId }),
|
||||
status: 500,
|
||||
code,
|
||||
errorName: error instanceof Error ? error.name : "UnknownError",
|
||||
message: redactString(message),
|
||||
...(kubernetesStatus(error) && {
|
||||
kubernetesStatus: kubernetesStatus(error),
|
||||
}),
|
||||
errorName: diagnostics.errorName,
|
||||
message: unknownFailure ? diagnostics.message : redactString(message),
|
||||
...(unknownFailure && diagnostics.stack && { stack: diagnostics.stack }),
|
||||
...(unknownFailure &&
|
||||
kubernetesStatus(error) && {
|
||||
kubernetesStatus: kubernetesStatus(error),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1781,10 +1834,36 @@ export function createApp(
|
||||
const workspace = await getWorkspace(id);
|
||||
if (subpath === "adopt" && request.method === "POST") {
|
||||
await requireCapability(identity, request, "kubernetes:write");
|
||||
const adopted = await requireAdoption().adopt(
|
||||
workspace.metadata.name,
|
||||
workspace.metadata.uid,
|
||||
);
|
||||
let adopted: WorkspaceAdoptionResult;
|
||||
try {
|
||||
adopted = await requireAdoption().adopt(
|
||||
workspace.metadata.name,
|
||||
workspace.metadata.uid,
|
||||
);
|
||||
} catch (error) {
|
||||
try {
|
||||
await audit(
|
||||
identity,
|
||||
request,
|
||||
"workspace.adopt",
|
||||
"failure",
|
||||
{
|
||||
route: path,
|
||||
requestId: requestIds.get(request) ?? makeRequestId(),
|
||||
},
|
||||
id,
|
||||
);
|
||||
} catch (auditError) {
|
||||
logRequestError(request, auditError, {
|
||||
event: "workspace.adopt.audit.failed",
|
||||
code: "AUDIT_APPEND_FAILED",
|
||||
message:
|
||||
"Failed workspace adoption audit event could not be appended",
|
||||
workspaceId: id,
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
await audit(
|
||||
identity,
|
||||
request,
|
||||
@@ -2202,6 +2281,14 @@ export function createApp(
|
||||
|
||||
function normalizeError(error: unknown): HttpError {
|
||||
if (error instanceof HttpError) return error;
|
||||
const kubernetes = kubernetesError(error);
|
||||
if (kubernetes)
|
||||
return new HttpError(
|
||||
kubernetes.status,
|
||||
"Kubernetes error",
|
||||
"KUBERNETES_ERROR",
|
||||
kubernetes.detail,
|
||||
);
|
||||
if (error instanceof WorkspaceNotFoundError)
|
||||
return new HttpError(404, "Not found", error.code, error.message);
|
||||
if (error instanceof OperationNotFoundError)
|
||||
|
||||
Reference in New Issue
Block a user