@impz/testing 6.0.0 → 6.0.2
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/README.md +4 -16
- package/dist/index.js +216 -25
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -79,22 +79,10 @@ await forward.listening;
|
|
|
79
79
|
const stream = await guest.connect(4000);
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
The guest
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
the process reads.
|
|
87
|
-
- A process that resets its connection drops the relay with no EOF.
|
|
88
|
-
- A forward that asks for port 0 gets a free port, and a second forward on a port in use fails with
|
|
89
|
-
`LISTEN_FAILED`.
|
|
90
|
-
- When the imp sleeps or goes away, the guest drops its connections, so a forward ends as `lost`. A
|
|
91
|
-
forward opened after the wake listens on the guest again.
|
|
92
|
-
|
|
93
|
-
It does not copy:
|
|
94
|
-
|
|
95
|
-
- unix socket forwards and ssh-agent forwarding, which it answers with `UNKNOWN_OP`
|
|
96
|
-
- the refusal of a port below 1024 to an image whose user is not root
|
|
97
|
-
- the limit of 16 clients waiting for impd, and the 10 s after which an unpaired client closes
|
|
82
|
+
The guest relays bytes and EOFs as the real guest agent's reverse forwards do, and drops its
|
|
83
|
+
connections when the imp sleeps.
|
|
84
|
+
[Testing against impd](https://github.com/zgeoff/impz/blob/main/docs/guides/testing-impd.md) lists
|
|
85
|
+
what it copies and what it does not.
|
|
98
86
|
|
|
99
87
|
## What this impd does not do
|
|
100
88
|
|
package/dist/index.js
CHANGED
|
@@ -21949,7 +21949,7 @@ import { join as join46 } from "path";
|
|
|
21949
21949
|
// packages/daemon/package.json
|
|
21950
21950
|
var package_default = {
|
|
21951
21951
|
name: "@impz/daemon",
|
|
21952
|
-
version: "6.0.
|
|
21952
|
+
version: "6.0.2",
|
|
21953
21953
|
private: true,
|
|
21954
21954
|
description: "impd: the imp host daemon",
|
|
21955
21955
|
type: "module",
|
|
@@ -26652,6 +26652,12 @@ import { FFIType, dlopen, read } from "bun:ffi";
|
|
|
26652
26652
|
var CORE_SYMBOLS = {
|
|
26653
26653
|
ioctl: { args: [FFIType.i32, FFIType.u64, FFIType.ptr], returns: FFIType.i32 },
|
|
26654
26654
|
lseek: { args: [FFIType.i32, FFIType.i64, FFIType.i32], returns: FFIType.i64 },
|
|
26655
|
+
mmap: {
|
|
26656
|
+
args: [FFIType.u64, FFIType.u64, FFIType.i32, FFIType.i32, FFIType.i32, FFIType.i64],
|
|
26657
|
+
returns: FFIType.i64
|
|
26658
|
+
},
|
|
26659
|
+
munmap: { args: [FFIType.u64, FFIType.u64], returns: FFIType.i32 },
|
|
26660
|
+
mlock: { args: [FFIType.u64, FFIType.u64], returns: FFIType.i32 },
|
|
26655
26661
|
__errno_location: { args: [], returns: FFIType.ptr }
|
|
26656
26662
|
};
|
|
26657
26663
|
var PIDFD_SYMBOLS = {
|
|
@@ -37248,8 +37254,9 @@ function createRamGovernor(deps) {
|
|
|
37248
37254
|
const listed = await deps.listAwake();
|
|
37249
37255
|
const awake = listed.filter((imp) => imp.id !== excludeId);
|
|
37250
37256
|
const byImp = new Map;
|
|
37251
|
-
|
|
37252
|
-
let
|
|
37257
|
+
const lockedMib = deps.readLockedMib?.() ?? 0;
|
|
37258
|
+
let usedMib = lockedMib;
|
|
37259
|
+
let effectiveMib = lockedMib;
|
|
37253
37260
|
const readings = await Promise.all(awake.map((imp) => readVmMib(imp.pid, imp.apiSocket)));
|
|
37254
37261
|
for (const [index, imp] of awake.entries()) {
|
|
37255
37262
|
const measured = readings[index] ?? 0;
|
|
@@ -38947,6 +38954,89 @@ async function setupVm(api, config) {
|
|
|
38947
38954
|
await api.putBalloon(VM_DEVICES.balloon);
|
|
38948
38955
|
}
|
|
38949
38956
|
|
|
38957
|
+
// packages/daemon/src/vm/templates/template-locks.ts
|
|
38958
|
+
import { match as match6 } from "ts-pattern";
|
|
38959
|
+
|
|
38960
|
+
// packages/daemon/src/vm/templates/can-admit-lock.ts
|
|
38961
|
+
var MAX_LOCKED_PERCENT = 10;
|
|
38962
|
+
var MIB3 = 1024 * 1024;
|
|
38963
|
+
function canAdmitLock(admission) {
|
|
38964
|
+
const maxLockedBytes = Math.floor(admission.ramBudgetMib * MAX_LOCKED_PERCENT / 100) * MIB3;
|
|
38965
|
+
return admission.heldBytes + admission.lockBytes <= maxLockedBytes;
|
|
38966
|
+
}
|
|
38967
|
+
|
|
38968
|
+
// packages/daemon/src/vm/templates/pick-lock-outcome.ts
|
|
38969
|
+
import { P as P3, match as match5 } from "ts-pattern";
|
|
38970
|
+
var MIB4 = 1024 * 1024;
|
|
38971
|
+
function pickLockOutcome(result, isCurrent) {
|
|
38972
|
+
return match5(result).with({ errno: P3.number }, (failed) => ({ kind: "failed", errno: failed.errno })).with({ declinedBytes: P3.number }, (declined) => ({
|
|
38973
|
+
kind: "declined",
|
|
38974
|
+
lockMib: Math.ceil(declined.declinedBytes / MIB4)
|
|
38975
|
+
})).with({ lock: P3.select() }, (lock) => isCurrent ? { kind: "held", lock, lockMib: Math.ceil(lock.lockedBytes / MIB4) } : { kind: "stale", lock }).exhaustive();
|
|
38976
|
+
}
|
|
38977
|
+
|
|
38978
|
+
// packages/daemon/src/vm/templates/template-locks.ts
|
|
38979
|
+
var MIB5 = 1024 * 1024;
|
|
38980
|
+
function createTemplateLocks(deps) {
|
|
38981
|
+
const locks = new Map;
|
|
38982
|
+
const locking = createSingleFlight();
|
|
38983
|
+
const sizedBytes = new Map;
|
|
38984
|
+
const state = { isStopping: false };
|
|
38985
|
+
const readHeldBytes = () => [...locks.values()].reduce((total, held) => total + held.lockedBytes, 0) + [...sizedBytes.values()].reduce((total, bytes) => total + bytes, 0);
|
|
38986
|
+
const runLock = async (target) => {
|
|
38987
|
+
const key = target.key.slice(0, 12);
|
|
38988
|
+
try {
|
|
38989
|
+
const result = await deps.lockMemory(target.memFile, (lockBytes) => {
|
|
38990
|
+
const isAdmitted = canAdmitLock({
|
|
38991
|
+
heldBytes: readHeldBytes(),
|
|
38992
|
+
lockBytes,
|
|
38993
|
+
ramBudgetMib: deps.ramBudgetMib
|
|
38994
|
+
});
|
|
38995
|
+
if (isAdmitted) {
|
|
38996
|
+
sizedBytes.set(target.buildId, lockBytes);
|
|
38997
|
+
}
|
|
38998
|
+
return isAdmitted;
|
|
38999
|
+
});
|
|
39000
|
+
match6(pickLockOutcome(result, deps.probeCurrent(target))).with({ kind: "failed" }, (failed) => {
|
|
39001
|
+
deps.logger.warning("boot template {key} memory not locked (errno {errno}): a restore reads what the host dropped", { key, errno: String(failed.errno) });
|
|
39002
|
+
}).with({ kind: "declined" }, (declined) => {
|
|
39003
|
+
deps.logger.info("boot template {key} memory not locked: {lockMib} MiB would take the locks past a tenth of the RAM budget", { key, lockMib: String(declined.lockMib) });
|
|
39004
|
+
}).with({ kind: "stale" }, (stale) => {
|
|
39005
|
+
stale.lock.release();
|
|
39006
|
+
}).with({ kind: "held" }, (held) => {
|
|
39007
|
+
locks.set(target.key, held.lock);
|
|
39008
|
+
deps.logger.info("boot template {key} memory locked: {lockMib} MiB", {
|
|
39009
|
+
key,
|
|
39010
|
+
lockMib: String(held.lockMib)
|
|
39011
|
+
});
|
|
39012
|
+
}).exhaustive();
|
|
39013
|
+
} catch (error) {
|
|
39014
|
+
deps.logger.warning("boot template {key} memory not locked: {error}", {
|
|
39015
|
+
key,
|
|
39016
|
+
error: getErrorMessage(error)
|
|
39017
|
+
});
|
|
39018
|
+
} finally {
|
|
39019
|
+
sizedBytes.delete(target.buildId);
|
|
39020
|
+
}
|
|
39021
|
+
};
|
|
39022
|
+
return {
|
|
39023
|
+
lock: (target) => state.isStopping || locks.has(target.key) ? Promise.resolve() : locking.run(target.buildId, () => runLock(target)),
|
|
39024
|
+
release: (key) => {
|
|
39025
|
+
locks.get(key)?.release();
|
|
39026
|
+
locks.delete(key);
|
|
39027
|
+
},
|
|
39028
|
+
readLockedMib: () => Math.ceil(readHeldBytes() / MIB5),
|
|
39029
|
+
stop: async () => {
|
|
39030
|
+
state.isStopping = true;
|
|
39031
|
+
await locking.waitForAll();
|
|
39032
|
+
for (const held of locks.values()) {
|
|
39033
|
+
held.release();
|
|
39034
|
+
}
|
|
39035
|
+
locks.clear();
|
|
39036
|
+
}
|
|
39037
|
+
};
|
|
39038
|
+
}
|
|
39039
|
+
|
|
38950
39040
|
// packages/daemon/src/vm/templates/boot-templates.ts
|
|
38951
39041
|
var TEMPLATE_BOOT_ARGS = [...BASE_BOOT_ARGS, "imp.template=1"].join(" ");
|
|
38952
39042
|
var TEMPLATE_ADDRESS = {
|
|
@@ -38965,7 +39055,7 @@ var BUILD_JAIL_ID = "tpl-build";
|
|
|
38965
39055
|
var BUILD_CPU = { limit: null, weight: 100 };
|
|
38966
39056
|
var PLACEHOLDER_BYTES = 1024 * 1024;
|
|
38967
39057
|
var PLACEHOLDER_NAME = "placeholder.ext4";
|
|
38968
|
-
var
|
|
39058
|
+
var MIB6 = 1024 * 1024;
|
|
38969
39059
|
var MIN_MISSES = 2;
|
|
38970
39060
|
var MAX_TEMPLATES = 4;
|
|
38971
39061
|
var MAX_BUILD_FAILURES = 3;
|
|
@@ -39039,6 +39129,7 @@ function createBootTemplates(deps) {
|
|
|
39039
39129
|
};
|
|
39040
39130
|
const listTemplateKeys = () => existsSync9(root) ? readdirSync3(root).filter((name) => name !== PLACEHOLDER_NAME && !name.startsWith(".")) : [];
|
|
39041
39131
|
const removeDir = (name) => {
|
|
39132
|
+
templateLocks.release(name);
|
|
39042
39133
|
const gone = join18(root, `.gone-${Bun.randomUUIDv7()}`);
|
|
39043
39134
|
try {
|
|
39044
39135
|
renameSync3(join18(root, name), gone);
|
|
@@ -39048,6 +39139,12 @@ function createBootTemplates(deps) {
|
|
|
39048
39139
|
rmSync15(gone, { recursive: true, force: true });
|
|
39049
39140
|
lastUsed.delete(name);
|
|
39050
39141
|
};
|
|
39142
|
+
const templateLocks = createTemplateLocks({
|
|
39143
|
+
lockMemory: deps.lockMemory,
|
|
39144
|
+
probeCurrent: (target) => readMeta(target.key)?.buildId === target.buildId,
|
|
39145
|
+
ramBudgetMib: deps.ramBudgetMib,
|
|
39146
|
+
logger: getImpdLogger("boot")
|
|
39147
|
+
});
|
|
39051
39148
|
const readLastUsed = (key) => {
|
|
39052
39149
|
const used = lastUsed.get(key);
|
|
39053
39150
|
if (used !== undefined) {
|
|
@@ -39138,7 +39235,7 @@ function createBootTemplates(deps) {
|
|
|
39138
39235
|
key: key.slice(0, 12),
|
|
39139
39236
|
durationMs: String(durationMs)
|
|
39140
39237
|
});
|
|
39141
|
-
|
|
39238
|
+
const files = {
|
|
39142
39239
|
key,
|
|
39143
39240
|
buildId,
|
|
39144
39241
|
vmstate: join18(root, key, "vmstate"),
|
|
@@ -39146,6 +39243,8 @@ function createBootTemplates(deps) {
|
|
|
39146
39243
|
systemDrivePath: meta.systemDrivePath,
|
|
39147
39244
|
placeholderPath: placeholder
|
|
39148
39245
|
};
|
|
39246
|
+
templateLocks.lock(files);
|
|
39247
|
+
return files;
|
|
39149
39248
|
};
|
|
39150
39249
|
const runBuild = async (shape, key) => {
|
|
39151
39250
|
const work = join18(root, `.build-${Bun.randomUUIDv7()}`);
|
|
@@ -39164,7 +39263,7 @@ function createBootTemplates(deps) {
|
|
|
39164
39263
|
memoryMib: shape.memoryMib,
|
|
39165
39264
|
maySleepImps: false
|
|
39166
39265
|
});
|
|
39167
|
-
return await (deps.diskBudget === undefined ? writeAdmitted() : deps.diskBudget.withRoom(shape.memoryMib *
|
|
39266
|
+
return await (deps.diskBudget === undefined ? writeAdmitted() : deps.diskBudget.withRoom(shape.memoryMib * MIB6, writeAdmitted));
|
|
39168
39267
|
} catch (error) {
|
|
39169
39268
|
if (progress.isBuilding) {
|
|
39170
39269
|
throw error;
|
|
@@ -39279,10 +39378,98 @@ function createBootTemplates(deps) {
|
|
|
39279
39378
|
const meta = readMeta(key);
|
|
39280
39379
|
return meta === null ? [] : [meta.systemDrivePath];
|
|
39281
39380
|
}),
|
|
39282
|
-
|
|
39381
|
+
lockKept: async () => {
|
|
39382
|
+
for (const key of listTemplateKeys()) {
|
|
39383
|
+
const ready = readReady(key);
|
|
39384
|
+
if (ready !== null) {
|
|
39385
|
+
await templateLocks.lock(ready);
|
|
39386
|
+
}
|
|
39387
|
+
}
|
|
39388
|
+
},
|
|
39389
|
+
readLockedMib: templateLocks.readLockedMib,
|
|
39390
|
+
stop: async () => {
|
|
39391
|
+
await Promise.all([templateLocks.stop(), building.waitForAll()]);
|
|
39392
|
+
}
|
|
39283
39393
|
};
|
|
39284
39394
|
}
|
|
39285
39395
|
|
|
39396
|
+
// packages/daemon/src/vm/templates/create-memory-lock.ts
|
|
39397
|
+
import { closeSync as closeSync8, fstatSync as fstatSync3, openSync as openSync6 } from "fs";
|
|
39398
|
+
var PROT_READ = 1;
|
|
39399
|
+
var MAP_SHARED = 1;
|
|
39400
|
+
async function createMemoryLock(path, onSized) {
|
|
39401
|
+
const libc = readLibc();
|
|
39402
|
+
if (libc === null) {
|
|
39403
|
+
return { errno: -1 };
|
|
39404
|
+
}
|
|
39405
|
+
const fd = openSync6(path, "r");
|
|
39406
|
+
try {
|
|
39407
|
+
const size = fstatSync3(fd).size;
|
|
39408
|
+
const runs = readDataRuns(libc, fd, size);
|
|
39409
|
+
if ("errno" in runs) {
|
|
39410
|
+
return runs;
|
|
39411
|
+
}
|
|
39412
|
+
const lockedBytes = runs.reduce((total, run) => total + run.length, 0);
|
|
39413
|
+
if (!onSized(lockedBytes)) {
|
|
39414
|
+
return { declinedBytes: lockedBytes };
|
|
39415
|
+
}
|
|
39416
|
+
const address = Number(libc.mmap(0, size, PROT_READ, MAP_SHARED, fd, 0));
|
|
39417
|
+
if (address === -1) {
|
|
39418
|
+
return { errno: readErrno() };
|
|
39419
|
+
}
|
|
39420
|
+
const mapping = { isMapped: true };
|
|
39421
|
+
const release = () => {
|
|
39422
|
+
if (mapping.isMapped) {
|
|
39423
|
+
mapping.isMapped = false;
|
|
39424
|
+
libc.munmap(address, size);
|
|
39425
|
+
}
|
|
39426
|
+
};
|
|
39427
|
+
for (const chunk of runs.flatMap((run) => splitRun(run))) {
|
|
39428
|
+
if (libc.mlock(address + chunk.offset, chunk.length) !== 0) {
|
|
39429
|
+
const errno = readErrno();
|
|
39430
|
+
release();
|
|
39431
|
+
return { errno };
|
|
39432
|
+
}
|
|
39433
|
+
await Bun.sleep(0);
|
|
39434
|
+
}
|
|
39435
|
+
return { lock: { lockedBytes, release } };
|
|
39436
|
+
} finally {
|
|
39437
|
+
closeSync8(fd);
|
|
39438
|
+
}
|
|
39439
|
+
}
|
|
39440
|
+
var SEEK_DATA2 = 3;
|
|
39441
|
+
var SEEK_HOLE2 = 4;
|
|
39442
|
+
var ENXIO2 = 6;
|
|
39443
|
+
function readDataRuns(libc, fd, size) {
|
|
39444
|
+
const runs = [];
|
|
39445
|
+
let offset = 0;
|
|
39446
|
+
while (offset < size) {
|
|
39447
|
+
const data = Number(libc.lseek(fd, offset, SEEK_DATA2));
|
|
39448
|
+
if (data < 0) {
|
|
39449
|
+
const errno = readErrno();
|
|
39450
|
+
if (errno === ENXIO2) {
|
|
39451
|
+
break;
|
|
39452
|
+
}
|
|
39453
|
+
return { errno };
|
|
39454
|
+
}
|
|
39455
|
+
const hole = Number(libc.lseek(fd, data, SEEK_HOLE2));
|
|
39456
|
+
if (hole < 0) {
|
|
39457
|
+
return { errno: readErrno() };
|
|
39458
|
+
}
|
|
39459
|
+
const end = Math.min(hole, size);
|
|
39460
|
+
runs.push({ offset: data, length: end - data });
|
|
39461
|
+
offset = end;
|
|
39462
|
+
}
|
|
39463
|
+
return runs;
|
|
39464
|
+
}
|
|
39465
|
+
var CHUNK_BYTES = 4 * 1024 * 1024;
|
|
39466
|
+
function splitRun(run) {
|
|
39467
|
+
return Array.from({ length: Math.ceil(run.length / CHUNK_BYTES) }, (_unused, index) => {
|
|
39468
|
+
const offset = run.offset + index * CHUNK_BYTES;
|
|
39469
|
+
return { offset, length: Math.min(CHUNK_BYTES, run.offset + run.length - offset) };
|
|
39470
|
+
});
|
|
39471
|
+
}
|
|
39472
|
+
|
|
39286
39473
|
// packages/daemon/src/vm/vmm/vm-stats.ts
|
|
39287
39474
|
import { readFileSync as readFileSync19, readdirSync as readdirSync4 } from "fs";
|
|
39288
39475
|
import { readFile as readFile3 } from "fs/promises";
|
|
@@ -40855,7 +41042,9 @@ function createDefaultTemplates(deps, cgroups) {
|
|
|
40855
41042
|
kernelPath: deps.config.kernelPath,
|
|
40856
41043
|
minGuestUptimeMs: deps.config.sleepMinGuestUptimeMs,
|
|
40857
41044
|
bootReservePercent: deps.config.bootReservePercent,
|
|
41045
|
+
ramBudgetMib: deps.config.ramBudgetMib,
|
|
40858
41046
|
buildVm: deps.vms.createTemplateVm,
|
|
41047
|
+
lockMemory: createMemoryLock,
|
|
40859
41048
|
taps: deps.taps,
|
|
40860
41049
|
jail: deps.config.jailerBin === null ? null : { uid: TEMPLATE_BUILD_UID, gid: TEMPLATE_BUILD_UID },
|
|
40861
41050
|
cgroups,
|
|
@@ -42464,12 +42653,12 @@ function readNumber(parsed, key) {
|
|
|
42464
42653
|
// packages/daemon/src/vm/vmm/jail.ts
|
|
42465
42654
|
import {
|
|
42466
42655
|
chmodSync as chmodSync6,
|
|
42467
|
-
closeSync as
|
|
42656
|
+
closeSync as closeSync9,
|
|
42468
42657
|
existsSync as existsSync12,
|
|
42469
42658
|
lchownSync as lchownSync2,
|
|
42470
42659
|
lstatSync as lstatSync3,
|
|
42471
42660
|
mkdirSync as mkdirSync14,
|
|
42472
|
-
openSync as
|
|
42661
|
+
openSync as openSync7,
|
|
42473
42662
|
readdirSync as readdirSync7,
|
|
42474
42663
|
rmSync as rmSync19,
|
|
42475
42664
|
statSync as statSync9,
|
|
@@ -42558,7 +42747,7 @@ function createJails(deps) {
|
|
|
42558
42747
|
};
|
|
42559
42748
|
const setupReadOnlyBind = async (source, target) => {
|
|
42560
42749
|
mkdirSync14(dirname9(target), { recursive: true });
|
|
42561
|
-
|
|
42750
|
+
closeSync9(openSync7(target, "a"));
|
|
42562
42751
|
await runMount(["mount", "--bind", source, target]);
|
|
42563
42752
|
await runMount(["mount", "-o", "remount,bind,ro,nosuid,nodev", target]);
|
|
42564
42753
|
};
|
|
@@ -42705,7 +42894,7 @@ function setupOwnership(paths, user, isDiskLate) {
|
|
|
42705
42894
|
}
|
|
42706
42895
|
function setupScratchFile(source, target, user) {
|
|
42707
42896
|
mkdirSync14(dirname9(target), { recursive: true });
|
|
42708
|
-
|
|
42897
|
+
closeSync9(openSync7(target, "wx", 384));
|
|
42709
42898
|
truncateSync3(target, statSync9(source).size);
|
|
42710
42899
|
lchownSync2(target, user.uid, user.gid);
|
|
42711
42900
|
}
|
|
@@ -44320,6 +44509,7 @@ function createImpdImps(config, deps, parts) {
|
|
|
44320
44509
|
readRamMib: readRamMibAsync,
|
|
44321
44510
|
readResidentMib,
|
|
44322
44511
|
readHeadroomMib,
|
|
44512
|
+
readLockedMib: () => imps.bootTemplates?.readLockedMib() ?? 0,
|
|
44323
44513
|
isBusy,
|
|
44324
44514
|
trySleepImp: imps.trySleepImp,
|
|
44325
44515
|
readDiskFullError: imps.readDiskFullError,
|
|
@@ -45796,7 +45986,7 @@ function readSettled2(row, imp) {
|
|
|
45796
45986
|
|
|
45797
45987
|
// packages/daemon/src/imp-ops/moves/move-sender.ts
|
|
45798
45988
|
import invariant4 from "tiny-invariant";
|
|
45799
|
-
import { match as
|
|
45989
|
+
import { match as match7 } from "ts-pattern";
|
|
45800
45990
|
|
|
45801
45991
|
// packages/daemon/src/platform/db/move-send-steps.ts
|
|
45802
45992
|
var MOVE_SEND_STEPS = [
|
|
@@ -46358,7 +46548,7 @@ function createMoveSender(deps) {
|
|
|
46358
46548
|
};
|
|
46359
46549
|
const recoverSend = async (imp, row) => {
|
|
46360
46550
|
const log = getImpdLogger("move").with({ imp: imp.name });
|
|
46361
|
-
await
|
|
46551
|
+
await match7(pickSendRecovery(imp.moveState, row?.step)).with("unmark", () => removeMark(imp)).with("wait", () => {
|
|
46362
46552
|
log.info("a prepared send waits for its send after a restart");
|
|
46363
46553
|
}).with("abort", async () => {
|
|
46364
46554
|
const isUndone = await resetSend(imp, row?.peer_url ?? null, row?.ticket ?? null);
|
|
@@ -46480,7 +46670,7 @@ function buildPublicError(name) {
|
|
|
46480
46670
|
}
|
|
46481
46671
|
function pickSendRecovery(moveState, step) {
|
|
46482
46672
|
if (moveState === "sending") {
|
|
46483
|
-
return
|
|
46673
|
+
return match7(step).with(undefined, () => "unmark").with("prepared", () => "wait").with("streaming", () => "abort").with("sent", () => "commit").exhaustive();
|
|
46484
46674
|
}
|
|
46485
46675
|
return moveState === "moved" && step === "sent" ? "commit" : "none";
|
|
46486
46676
|
}
|
|
@@ -46532,7 +46722,7 @@ function createMoveService(deps) {
|
|
|
46532
46722
|
// packages/daemon/src/surface/moves/create-move-peer-handler.ts
|
|
46533
46723
|
import { OpenAPIHandler } from "@orpc/openapi/fetch";
|
|
46534
46724
|
import { ORPCError as ORPCError7, implement as implement3, onError as onError3 } from "@orpc/server";
|
|
46535
|
-
import { match as
|
|
46725
|
+
import { match as match8 } from "ts-pattern";
|
|
46536
46726
|
function createMovePeerHandler(deps) {
|
|
46537
46727
|
const withTicket = async (step, context, call) => {
|
|
46538
46728
|
const startedAt = deps.now();
|
|
@@ -46583,7 +46773,7 @@ function createMovePeerHandler(deps) {
|
|
|
46583
46773
|
const respondReceive = async (context) => {
|
|
46584
46774
|
try {
|
|
46585
46775
|
const received = await withTicket("receive", context, (checked) => deps.calls.receive(checked, context.request));
|
|
46586
|
-
return
|
|
46776
|
+
return match8(received).with({ kind: "part" }, (sent) => Response.json({ part: sent.part }, { status: 202 })).with({ kind: "receipt" }, (sent) => Response.json(sent.receipt)).exhaustive();
|
|
46587
46777
|
} catch (error) {
|
|
46588
46778
|
const failure = toPeerError(error);
|
|
46589
46779
|
return Response.json(encodePeerError(failure), { status: failure.status });
|
|
@@ -47296,9 +47486,9 @@ function printSweep(logger, result, orphans) {
|
|
|
47296
47486
|
}
|
|
47297
47487
|
logger.info("kept {length} orphans the database does not name; `impz gc --orphans --dry-run` lists what `impz gc --orphans` would retire", { length: String(result.kept.length) });
|
|
47298
47488
|
}
|
|
47299
|
-
var
|
|
47489
|
+
var MIB7 = 1024 ** 2;
|
|
47300
47490
|
function formatOrphan(orphan) {
|
|
47301
|
-
const size = `${(orphan.bytes /
|
|
47491
|
+
const size = `${(orphan.bytes / MIB7).toFixed(1)} MiB`;
|
|
47302
47492
|
const created = orphan.createdAt?.toISOString() ?? "unknown";
|
|
47303
47493
|
const snapshots = orphan.snapshots.length === 0 ? "none" : orphan.snapshots.join(", ");
|
|
47304
47494
|
return `orphan ${orphan.kind} ${orphan.id} (${orphan.location}): ${size}, created ${created}, snapshots: ${snapshots}`;
|
|
@@ -48781,11 +48971,11 @@ class ImageLimitError extends ImpdError {
|
|
|
48781
48971
|
|
|
48782
48972
|
// packages/daemon/src/imps/images/rootfs/rootfs-plan.ts
|
|
48783
48973
|
var GIB3 = 1024 ** 3;
|
|
48784
|
-
var
|
|
48974
|
+
var MIB8 = 1024 ** 2;
|
|
48785
48975
|
var ROOTFS_MIN_BYTES = 4 * GIB3;
|
|
48786
48976
|
var ROOTFS_SPARE_BYTES = 2 * GIB3;
|
|
48787
48977
|
var BYTES_PER_INODE = 16384;
|
|
48788
|
-
var ROOTFS_OVERHEAD_MIN_BYTES = 128 *
|
|
48978
|
+
var ROOTFS_OVERHEAD_MIN_BYTES = 128 * MIB8;
|
|
48789
48979
|
var ROOTFS_OVERHEAD_SHARE = 64;
|
|
48790
48980
|
function planRootfs(tree) {
|
|
48791
48981
|
const wanted = Math.ceil((tree.bytes * 1.2 + ROOTFS_SPARE_BYTES) / GIB3) * GIB3;
|
|
@@ -49441,7 +49631,7 @@ async function readOciConfig(ref, signal, run) {
|
|
|
49441
49631
|
}
|
|
49442
49632
|
|
|
49443
49633
|
// packages/daemon/src/imps/images/rootfs/write-image-config.ts
|
|
49444
|
-
import { closeSync as
|
|
49634
|
+
import { closeSync as closeSync10, constants as constants5, lstatSync as lstatSync5, mkdirSync as mkdirSync21, openSync as openSync8, rmSync as rmSync25, writeSync as writeSync3 } from "fs";
|
|
49445
49635
|
import { join as join29 } from "path";
|
|
49446
49636
|
function writeImageConfig(root, contents) {
|
|
49447
49637
|
const etc = join29(root, "etc");
|
|
@@ -49450,11 +49640,11 @@ function writeImageConfig(root, contents) {
|
|
|
49450
49640
|
createDirectory(directory, "/etc/imp");
|
|
49451
49641
|
const file = join29(directory, "image.json");
|
|
49452
49642
|
rmSync25(file, { recursive: true, force: true });
|
|
49453
|
-
const fd =
|
|
49643
|
+
const fd = openSync8(file, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 420);
|
|
49454
49644
|
try {
|
|
49455
49645
|
writeSync3(fd, contents);
|
|
49456
49646
|
} finally {
|
|
49457
|
-
|
|
49647
|
+
closeSync10(fd);
|
|
49458
49648
|
}
|
|
49459
49649
|
}
|
|
49460
49650
|
function createDirectory(path, name) {
|
|
@@ -50843,14 +51033,14 @@ function expandScopes(asked) {
|
|
|
50843
51033
|
}
|
|
50844
51034
|
|
|
50845
51035
|
// packages/daemon/src/platform/http/render-html.ts
|
|
50846
|
-
import { P as
|
|
51036
|
+
import { P as P4, match as match9 } from "ts-pattern";
|
|
50847
51037
|
var MARKUP = Symbol("markup");
|
|
50848
51038
|
function renderHTML(strings, ...parts) {
|
|
50849
51039
|
const html = parts.reduce((text, part, index) => `${text}${toHTML(part)}${strings[index + 1] ?? ""}`, strings[0] ?? "");
|
|
50850
51040
|
return { [MARKUP]: true, html };
|
|
50851
51041
|
}
|
|
50852
51042
|
function toHTML(part) {
|
|
50853
|
-
return
|
|
51043
|
+
return match9(part).with(P4.string, (text) => escapeHTML(text)).with(P4.array(), (list) => list.map((each) => each.html).join("")).with({ html: P4.string }, (markup) => markup.html).exhaustive();
|
|
50854
51044
|
}
|
|
50855
51045
|
function escapeHTML(text) {
|
|
50856
51046
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -57034,6 +57224,7 @@ async function startImpdParts(config, boundaries, plan) {
|
|
|
57034
57224
|
key: key.slice(0, 12)
|
|
57035
57225
|
});
|
|
57036
57226
|
}
|
|
57227
|
+
imps.bootTemplates?.lockKept();
|
|
57037
57228
|
});
|
|
57038
57229
|
await runStep("system-drives", async () => {
|
|
57039
57230
|
const removed = await removeUnusedDrives(db, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@impz/testing",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.2",
|
|
4
4
|
"description": "A real impd on stand-ins, for Bun tests of code that drives impd through @impz/client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@bufbuild/protobuf": "^2.16.0",
|
|
23
23
|
"@logtape/logtape": "^2.4.2",
|
|
24
24
|
"@modelcontextprotocol/server": "^2.3.1",
|
|
25
|
-
"@opentelemetry/api": "^1.
|
|
25
|
+
"@opentelemetry/api": "^1.10.0",
|
|
26
26
|
"@orpc/client": "^1.15.7",
|
|
27
27
|
"@orpc/contract": "^1.15.7",
|
|
28
28
|
"@orpc/openapi": "^1.15.7",
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
"ts-pattern": "^5.9.2",
|
|
43
43
|
"xstate": "^5.33.3",
|
|
44
44
|
"zod": "^4.6.7",
|
|
45
|
-
"@impz/client": "6.0.
|
|
45
|
+
"@impz/client": "6.0.2"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"bun": ">=1.4.3"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@opentelemetry/exporter-metrics-otlp-proto": "^0.
|
|
52
|
-
"@opentelemetry/exporter-trace-otlp-proto": "^0.
|
|
51
|
+
"@opentelemetry/exporter-metrics-otlp-proto": "^0.300.0",
|
|
52
|
+
"@opentelemetry/exporter-trace-otlp-proto": "^0.300.0",
|
|
53
53
|
"@opentelemetry/resources": "^2.12.0",
|
|
54
54
|
"@opentelemetry/sdk-metrics": "^2.12.0",
|
|
55
55
|
"@opentelemetry/sdk-trace-base": "^2.12.0",
|