* Delete a Coder workspace, retrying across transient build states. * * This is used for "cancel creation" because aborting the local `coder create` * process does not guarantee the control-plane build is canceled.
(
name: string,
options?: {
timeoutMs?: number;
signal?: AbortSignal;
/**
* If true, treat an initial "not found" as inconclusive and keep polling.
* This avoids races where `coder create` finishes server-side after mux aborts the CLI.
*/
waitForExistence?: boolean;
/**
* When `waitForExistence` is true: if we only see "not found" for this many ms
* without ever observing the workspace exist, treat it as success and return early.
* Defaults to `timeoutMs` (no separate short-circuit).
*/
waitForExistenceTimeoutMs?: number;
}
)
| 1386 | * process does not guarantee the control-plane build is canceled. |
| 1387 | */ |
| 1388 | async deleteWorkspaceEventually( |
| 1389 | name: string, |
| 1390 | options?: { |
| 1391 | timeoutMs?: number; |
| 1392 | signal?: AbortSignal; |
| 1393 | /** |
| 1394 | * If true, treat an initial "not found" as inconclusive and keep polling. |
| 1395 | * This avoids races where `coder create` finishes server-side after mux aborts the CLI. |
| 1396 | */ |
| 1397 | waitForExistence?: boolean; |
| 1398 | /** |
| 1399 | * When `waitForExistence` is true: if we only see "not found" for this many ms |
| 1400 | * without ever observing the workspace exist, treat it as success and return early. |
| 1401 | * Defaults to `timeoutMs` (no separate short-circuit). |
| 1402 | */ |
| 1403 | waitForExistenceTimeoutMs?: number; |
| 1404 | } |
| 1405 | ): Promise<Result<void>> { |
| 1406 | const timeoutMs = options?.timeoutMs ?? 60_000; |
| 1407 | const startTime = Date.now(); |
| 1408 | |
| 1409 | // Safety: never delete Coder workspaces mux didn't create. |
| 1410 | // Mux-created workspaces always use the mux- prefix. |
| 1411 | if (!name.startsWith("mux-")) { |
| 1412 | log.warn("Refusing to delete Coder workspace without mux- prefix", { name }); |
| 1413 | return Ok(undefined); |
| 1414 | } |
| 1415 | |
| 1416 | const isTimedOut = () => Date.now() - startTime > timeoutMs; |
| 1417 | const remainingMs = () => Math.max(0, timeoutMs - (Date.now() - startTime)); |
| 1418 | |
| 1419 | const unstableStates = new Set<CoderWorkspaceStatus>([ |
| 1420 | "starting", |
| 1421 | "pending", |
| 1422 | "stopping", |
| 1423 | "canceling", |
| 1424 | ]); |
| 1425 | |
| 1426 | let sawWorkspaceExist = false; |
| 1427 | let lastError: string | undefined; |
| 1428 | let attempt = 0; |
| 1429 | |
| 1430 | while (!isTimedOut()) { |
| 1431 | if (options?.signal?.aborted) { |
| 1432 | return Err("Delete operation aborted"); |
| 1433 | } |
| 1434 | |
| 1435 | const statusResult = await this.getWorkspaceStatus(name, { |
| 1436 | timeoutMs: Math.min(remainingMs(), 10_000), |
| 1437 | signal: options?.signal, |
| 1438 | }); |
| 1439 | |
| 1440 | if (statusResult.kind === "ok") { |
| 1441 | sawWorkspaceExist = true; |
| 1442 | |
| 1443 | if (statusResult.status === "deleted" || statusResult.status === "deleting") { |
| 1444 | return Ok(undefined); |
| 1445 | } |
no test coverage detected