@blaxel/core 0.3.13 → 0.3.18-dev.283
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/client/sdk.gen.js +105 -3
- package/dist/cjs/common/settings.js +28 -3
- package/dist/cjs/common/settings.test.js +50 -0
- package/dist/cjs/common/shell.js +18 -0
- package/dist/cjs/sandbox/filesystem/filesystem.js +31 -11
- package/dist/cjs/sandbox/filesystem/filesystem.test.js +207 -2
- package/dist/cjs/sandbox/sandbox.js +124 -0
- package/dist/cjs/types/client/sdk.gen.d.ts +31 -1
- package/dist/cjs/types/client/types.gen.d.ts +368 -7
- package/dist/cjs/types/common/settings.d.ts +15 -0
- package/dist/cjs/types/common/shell.d.ts +13 -0
- package/dist/cjs/types/sandbox/sandbox.d.ts +56 -1
- package/dist/cjs/types/sandbox/schedule.d.ts +2 -2
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/client/sdk.gen.js +105 -3
- package/dist/cjs-browser/common/settings.js +28 -3
- package/dist/cjs-browser/common/settings.test.js +50 -0
- package/dist/cjs-browser/common/shell.js +18 -0
- package/dist/cjs-browser/sandbox/filesystem/filesystem.js +31 -11
- package/dist/cjs-browser/sandbox/filesystem/filesystem.test.js +207 -2
- package/dist/cjs-browser/sandbox/sandbox.js +124 -0
- package/dist/cjs-browser/types/client/sdk.gen.d.ts +31 -1
- package/dist/cjs-browser/types/client/types.gen.d.ts +368 -7
- package/dist/cjs-browser/types/common/settings.d.ts +15 -0
- package/dist/cjs-browser/types/common/shell.d.ts +13 -0
- package/dist/cjs-browser/types/sandbox/sandbox.d.ts +56 -1
- package/dist/cjs-browser/types/sandbox/schedule.d.ts +2 -2
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/client/sdk.gen.js +96 -0
- package/dist/esm/common/settings.js +28 -3
- package/dist/esm/common/settings.test.js +50 -0
- package/dist/esm/common/shell.js +15 -0
- package/dist/esm/sandbox/filesystem/filesystem.js +31 -11
- package/dist/esm/sandbox/filesystem/filesystem.test.js +208 -3
- package/dist/esm/sandbox/sandbox.js +125 -1
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/client/sdk.gen.js +96 -0
- package/dist/esm-browser/common/settings.js +28 -3
- package/dist/esm-browser/common/settings.test.js +50 -0
- package/dist/esm-browser/common/shell.js +15 -0
- package/dist/esm-browser/sandbox/filesystem/filesystem.js +31 -11
- package/dist/esm-browser/sandbox/filesystem/filesystem.test.js +208 -3
- package/dist/esm-browser/sandbox/sandbox.js +125 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSandbox, createSandboxSnapshot, deleteSandbox, deleteSandboxSnapshot, forkSandbox, getSandbox, getSandboxByExternalId, listSandboxes, listSandboxSnapshots, updateSandbox } from "../client/index.js";
|
|
1
|
+
import { archiveSandbox, createSandbox, createSandboxSnapshot, deleteSandbox, deleteSandboxSnapshot, forkSandbox, getSandbox, getSandboxByExternalId, listSandboxes, listSandboxSnapshots, restoreSandboxSnapshot, unarchiveSandbox, updateSandbox } from "../client/index.js";
|
|
2
2
|
import { logger } from "../common/logger.js";
|
|
3
3
|
import { backoffDelayMs } from "../common/transient-retry.js";
|
|
4
4
|
import { createPaginatedList } from "../common/pagination.js";
|
|
@@ -31,6 +31,23 @@ const TRANSIENT_SANDBOX_STATUSES = new Set([
|
|
|
31
31
|
]);
|
|
32
32
|
const TRANSIENT_STATUS_MAX_WAIT_MS = 30_000;
|
|
33
33
|
const TRANSIENT_STATUS_POLL_MS = 500;
|
|
34
|
+
// Archiving a filesystem, and restoring it, take as long as that filesystem is
|
|
35
|
+
// big — minutes for a few gigabytes.
|
|
36
|
+
const ARCHIVE_MAX_WAIT_MS = 1_800_000;
|
|
37
|
+
const ARCHIVE_WAIT_POLL_MS = 2_000;
|
|
38
|
+
// An archive is done when the sandbox is ARCHIVED; it is still under way while
|
|
39
|
+
// the record holds one of these.
|
|
40
|
+
const ARCHIVING_STATUSES = new Set(["ARCHIVING"]);
|
|
41
|
+
// A restore is done when the sandbox is DEPLOYED again; the instance is recreated
|
|
42
|
+
// before the archived filesystem is written back over its image.
|
|
43
|
+
const UNARCHIVING_STATUSES = new Set(["UNARCHIVING", "DEPLOYING", "BUILDING", "UPLOADING"]);
|
|
44
|
+
// The status the sandbox holds before the operation moves it, tolerated only
|
|
45
|
+
// while the operation is starting: an archive that fails hands the sandbox back
|
|
46
|
+
// as DEPLOYED and a restore that fails leaves it ARCHIVED, so reading the entry
|
|
47
|
+
// status once the operation has begun means it is over, not still running.
|
|
48
|
+
const ARCHIVE_ENTRY_STATUS = "DEPLOYED";
|
|
49
|
+
const UNARCHIVE_ENTRY_STATUS = "ARCHIVED";
|
|
50
|
+
const ARCHIVE_ENTRY_MAX_WAIT_MS = 30_000;
|
|
34
51
|
// A create that outlives the edge's 60s origin-read timeout gets a 504 from
|
|
35
52
|
// CloudFront while the control plane keeps deploying the sandbox for up to
|
|
36
53
|
// 300s (ENG-3662 timeout ladder inversion). The 504 body is edge HTML with no
|
|
@@ -170,6 +187,24 @@ export class SandboxInstance {
|
|
|
170
187
|
throwOnError: true,
|
|
171
188
|
});
|
|
172
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Restore this sandbox to one of its own snapshots. The sandbox keeps its
|
|
192
|
+
* name, its URLs and its previews: the running instance is torn down and
|
|
193
|
+
* rebuilt from the snapshot, so everything written since it was taken is
|
|
194
|
+
* lost unless it was snapshotted too.
|
|
195
|
+
*
|
|
196
|
+
* The restore is asked for without waiting on the guest, the same way a fork
|
|
197
|
+
* is: connections to a sandbox still resuming are retried by the gateway.
|
|
198
|
+
*
|
|
199
|
+
* @param snapshotId - ID of the snapshot to restore this sandbox to.
|
|
200
|
+
*/
|
|
201
|
+
async restore(snapshotId) {
|
|
202
|
+
const { data } = await restoreSandboxSnapshot({
|
|
203
|
+
path: { sandboxName: this.metadata.name, snapshotId },
|
|
204
|
+
throwOnError: true,
|
|
205
|
+
});
|
|
206
|
+
return data;
|
|
207
|
+
}
|
|
173
208
|
/**
|
|
174
209
|
* Fork this sandbox into a new sandbox or application.
|
|
175
210
|
*
|
|
@@ -318,6 +353,95 @@ export class SandboxInstance {
|
|
|
318
353
|
}
|
|
319
354
|
return instance;
|
|
320
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Archive a sandbox: keep its filesystem, stop the sandbox.
|
|
358
|
+
*
|
|
359
|
+
* The filesystem changes made over the image are exported to the archive store
|
|
360
|
+
* and the sandbox is shut down; memory and running processes are lost, and the
|
|
361
|
+
* saved processes start again from their configuration when the sandbox is
|
|
362
|
+
* unarchived. The export runs in the background: this waits until the sandbox
|
|
363
|
+
* is ARCHIVED, pass `{ wait: false }` to return as soon as it is launched.
|
|
364
|
+
*/
|
|
365
|
+
static async archive(sandboxName, options = {}) {
|
|
366
|
+
const { data } = await archiveSandbox({
|
|
367
|
+
path: { sandboxName },
|
|
368
|
+
throwOnError: true,
|
|
369
|
+
});
|
|
370
|
+
return SandboxInstance.waitForArchiveStatus(sandboxName, data, "ARCHIVED", ARCHIVING_STATUSES, ARCHIVE_ENTRY_STATUS, "archive", options);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Archive this sandbox: keep its filesystem, stop the sandbox.
|
|
374
|
+
*
|
|
375
|
+
* @see SandboxInstance.archive
|
|
376
|
+
*/
|
|
377
|
+
async archive(options = {}) {
|
|
378
|
+
const instance = await SandboxInstance.archive(this.metadata.name, options);
|
|
379
|
+
this.refreshFrom(instance);
|
|
380
|
+
return this;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Recreate an archived sandbox from its archive.
|
|
384
|
+
*
|
|
385
|
+
* The sandbox is started again from its image, and the archived filesystem is
|
|
386
|
+
* written back over it. The sandbox answers, and its terminal is reachable, while the archived
|
|
387
|
+
* filesystem is written back over its image. This waits until the restore is
|
|
388
|
+
* done and the saved processes are running again; pass `{ wait: false }` to
|
|
389
|
+
* return while the sandbox is still UNARCHIVING.
|
|
390
|
+
*/
|
|
391
|
+
static async unarchive(sandboxName, options = {}) {
|
|
392
|
+
const { data } = await unarchiveSandbox({
|
|
393
|
+
path: { sandboxName },
|
|
394
|
+
throwOnError: true,
|
|
395
|
+
});
|
|
396
|
+
return SandboxInstance.waitForArchiveStatus(sandboxName, data, "DEPLOYED", UNARCHIVING_STATUSES, UNARCHIVE_ENTRY_STATUS, "unarchive", options);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Recreate this sandbox from its archive.
|
|
400
|
+
*
|
|
401
|
+
* @see SandboxInstance.unarchive
|
|
402
|
+
*/
|
|
403
|
+
async unarchive(options = {}) {
|
|
404
|
+
const instance = await SandboxInstance.unarchive(this.metadata.name, options);
|
|
405
|
+
this.refreshFrom(instance);
|
|
406
|
+
return this;
|
|
407
|
+
}
|
|
408
|
+
// The subsystems (fs, process, previews, ...) hold the configuration object
|
|
409
|
+
// this instance was built with, so a refresh writes into it rather than
|
|
410
|
+
// replacing it. Anything the read did not carry, the forced URL of a session
|
|
411
|
+
// above all, is kept.
|
|
412
|
+
refreshFrom(instance) {
|
|
413
|
+
Object.assign(this.sandbox, instance.sandbox);
|
|
414
|
+
}
|
|
415
|
+
static async waitForArchiveStatus(sandboxName, launched, target, pending, entry, action, { wait = true, maxWait = ARCHIVE_MAX_WAIT_MS, interval = ARCHIVE_WAIT_POLL_MS }) {
|
|
416
|
+
if (!wait || launched.status === target) {
|
|
417
|
+
return SandboxInstance.attachH2Session(new SandboxInstance(launched));
|
|
418
|
+
}
|
|
419
|
+
const deadline = Date.now() + maxWait;
|
|
420
|
+
// The status the sandbox is given back at is tolerated while the operation
|
|
421
|
+
// turns into a status change, but never past the wait the caller asked for.
|
|
422
|
+
const entryDeadline = Date.now() + Math.min(ARCHIVE_ENTRY_MAX_WAIT_MS, maxWait);
|
|
423
|
+
let started = false;
|
|
424
|
+
for (;;) {
|
|
425
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
426
|
+
const instance = await SandboxInstance.get(sandboxName);
|
|
427
|
+
const status = instance.status;
|
|
428
|
+
if (status === target) {
|
|
429
|
+
return instance;
|
|
430
|
+
}
|
|
431
|
+
if (pending.has(status ?? "")) {
|
|
432
|
+
started = true;
|
|
433
|
+
}
|
|
434
|
+
else if (status === entry && !started && Date.now() < entryDeadline) {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (!pending.has(status ?? "")) {
|
|
438
|
+
throw new Error(`Sandbox ${sandboxName} is ${status} while it should ${action}`);
|
|
439
|
+
}
|
|
440
|
+
if (Date.now() >= deadline) {
|
|
441
|
+
throw new Error(`Sandbox ${sandboxName} is still ${status} after waiting ${Math.round(maxWait / 1000)}s for it to ${action}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
321
445
|
static async get(sandboxName) {
|
|
322
446
|
const { data } = await getSandbox({
|
|
323
447
|
path: {
|