@molecule/api-code-sandbox-flyio 1.0.11 → 1.0.12
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/provider.d.ts.map +1 -1
- package/dist/provider.js +56 -12
- package/package.json +1 -1
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAUV,eAAe,EAEhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAsBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAEV,WAAW,EAUZ,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAUV,eAAe,EAEhB,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAgC,MAAM,UAAU,CAAA;AAsBrE,OAAO,EAAmD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhG,OAAO,KAAK,EAEV,WAAW,EAUZ,MAAM,YAAY,CAAA;AAs2DnB;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,YAAY,EACrB,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,GACzB,eAAe,CAEjB"}
|
package/dist/provider.js
CHANGED
|
@@ -90,6 +90,12 @@ const IMPORT_EXTRACT_TIMEOUT_MS = 55_000;
|
|
|
90
90
|
* the gzip pass before transfer is what keeps that count reasonable.
|
|
91
91
|
*/
|
|
92
92
|
const IMPORT_WRITE_CHUNK_BASE64 = 12_000;
|
|
93
|
+
/**
|
|
94
|
+
* Expiry for the presigned URLs {@link Sandbox.importFiles} uses to hand an
|
|
95
|
+
* archive to the object store and back to the Machine. Short — the upload and
|
|
96
|
+
* the single download happen back-to-back.
|
|
97
|
+
*/
|
|
98
|
+
const IMPORT_PRESIGN_EXPIRY_SECONDS = 3600;
|
|
93
99
|
/** Extra seconds allowed for the probe exec beyond its connection budget. */
|
|
94
100
|
const PROBE_EXEC_HEADROOM_SECONDS = 10;
|
|
95
101
|
/**
|
|
@@ -1495,25 +1501,63 @@ class FlyioSandboxProvider {
|
|
|
1495
1501
|
const parts = [];
|
|
1496
1502
|
for await (const chunk of archive)
|
|
1497
1503
|
parts.push(Buffer.from(chunk));
|
|
1498
|
-
const
|
|
1499
|
-
// The temp tar lives on the WORKSPACE VOLUME, not /tmp. The chunked
|
|
1500
|
-
// write and the extract are separate exec calls, and a Machine with
|
|
1501
|
-
// autostop=suspend can cold-start between them — which recreates the
|
|
1502
|
-
// rootfs from the image (wiping /tmp) while the ext4 volume at
|
|
1503
|
-
// /workspace persists. A /tmp temp file vanished mid-import; the volume
|
|
1504
|
-
// one survives.
|
|
1505
|
-
const tmp = `${WORKSPACE_PATH}/.mol-import-${machineId}.tgz`;
|
|
1506
|
-
const quotedTmp = shellQuote(tmp);
|
|
1504
|
+
const gz = gzipSync(Buffer.concat(parts));
|
|
1507
1505
|
const quotedPath = shellQuote(path);
|
|
1508
1506
|
const failIfError = (result, step) => {
|
|
1509
1507
|
if (result.exitCode !== 0) {
|
|
1510
1508
|
throw new Error(t('codeSandbox.flyio.error.importFailed', { path, step, error: result.stderr }, {
|
|
1511
|
-
defaultValue: `Failed to import files into ${path} (${step}): ${result.stderr}`,
|
|
1509
|
+
defaultValue: `Failed to import files into ${path} (${step}): ${result.stderr || result.stdout}`,
|
|
1512
1510
|
}));
|
|
1513
1511
|
}
|
|
1514
1512
|
};
|
|
1515
|
-
//
|
|
1516
|
-
//
|
|
1513
|
+
// PREFERRED: upload the archive to the object store and have the Machine
|
|
1514
|
+
// PULL it with a single curl. Fly's exec endpoint is rate-limited (~5/s)
|
|
1515
|
+
// and caps each command near 15 KB, so streaming a multi-MB tar through
|
|
1516
|
+
// it as base64 chunks triggers a 429 storm that never finishes. A
|
|
1517
|
+
// presigned download is ONE request — the same pull pattern templates use
|
|
1518
|
+
// (see buildRestoreCommand). Falls back to chunked exec when no store is
|
|
1519
|
+
// configured (e.g. unit tests).
|
|
1520
|
+
const store = provider.templateContext().store;
|
|
1521
|
+
if (store) {
|
|
1522
|
+
const key = `${store.prefix}/.imports/${machineId}-${Date.now().toString(36)}${Math.floor(Math.random() * 1e9).toString(36)}.tar.gz`;
|
|
1523
|
+
try {
|
|
1524
|
+
const putUrl = await store.presignPut(key, IMPORT_PRESIGN_EXPIRY_SECONDS);
|
|
1525
|
+
const putRes = await fetch(putUrl, { method: 'PUT', body: gz });
|
|
1526
|
+
if (!putRes.ok) {
|
|
1527
|
+
throw new Error(`presigned PUT to the object store failed: ${putRes.status}`);
|
|
1528
|
+
}
|
|
1529
|
+
const getUrl = await store.presignGet(key, IMPORT_PRESIGN_EXPIRY_SECONDS);
|
|
1530
|
+
const result = await this.exec([
|
|
1531
|
+
'set -e',
|
|
1532
|
+
`mkdir -p ${quotedPath}`,
|
|
1533
|
+
'archive=$(mktemp /tmp/mol-import-XXXXXX.tar.gz)',
|
|
1534
|
+
`curl --fail-with-body -sS --retry 3 --retry-connrefused -o "$archive" ${shellQuote(getUrl)}`,
|
|
1535
|
+
`tar -C ${quotedPath} --no-same-owner --no-same-permissions -xzf "$archive"`,
|
|
1536
|
+
'rm -f "$archive"',
|
|
1537
|
+
].join('\n'), { timeout: IMPORT_EXTRACT_TIMEOUT_MS });
|
|
1538
|
+
// Best-effort: the temp object expires anyway, but delete it promptly.
|
|
1539
|
+
store.remove([key]).catch((error) => {
|
|
1540
|
+
logger.debug('importFiles temp object cleanup failed', { key, error });
|
|
1541
|
+
});
|
|
1542
|
+
failIfError(result, 'download');
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
catch (error) {
|
|
1546
|
+
// The store path is unavailable or failed — fall through to the
|
|
1547
|
+
// chunked-exec transport rather than failing the whole import.
|
|
1548
|
+
logger.warn('importFiles via object store failed; falling back to chunked exec', {
|
|
1549
|
+
error,
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
// FALLBACK: chunked base64 over exec. Only used when no object store is
|
|
1554
|
+
// configured. The temp tar lives on the WORKSPACE VOLUME, not /tmp — the
|
|
1555
|
+
// chunked write and the extract are separate exec calls and a Machine
|
|
1556
|
+
// with autostop can cold-start between them, wiping /tmp (rootfs) while
|
|
1557
|
+
// the ext4 volume at /workspace persists.
|
|
1558
|
+
const base64 = gz.toString('base64');
|
|
1559
|
+
const tmp = `${WORKSPACE_PATH}/.mol-import-${machineId}.tgz`;
|
|
1560
|
+
const quotedTmp = shellQuote(tmp);
|
|
1517
1561
|
failIfError(await this.exec(`printf %s ${shellQuote(base64.slice(0, IMPORT_WRITE_CHUNK_BASE64))} | base64 -d > ${quotedTmp}`, { timeout: FILE_OP_TIMEOUT_MS }), 'write');
|
|
1518
1562
|
for (let i = IMPORT_WRITE_CHUNK_BASE64; i < base64.length; i += IMPORT_WRITE_CHUNK_BASE64) {
|
|
1519
1563
|
failIfError(await this.exec(`printf %s ${shellQuote(base64.slice(i, i + IMPORT_WRITE_CHUNK_BASE64))} | base64 -d >> ${quotedTmp}`, { timeout: FILE_OP_TIMEOUT_MS }), 'write');
|