@vm0/runner 3.11.1 → 3.11.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/index.js +187 -125
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -33,6 +33,8 @@ var runtimePaths = {
|
|
|
33
33
|
};
|
|
34
34
|
var VM_WORKSPACE_PREFIX = "vm0-";
|
|
35
35
|
var runnerPaths = {
|
|
36
|
+
/** Base directory used for snapshot generation (baseDir + /snapshot-gen) */
|
|
37
|
+
snapshotBaseDir: (baseDir) => path.join(baseDir, "snapshot-gen"),
|
|
36
38
|
/** Overlay pool directory for pre-warmed VM overlays */
|
|
37
39
|
overlayPool: (baseDir) => path.join(baseDir, "overlay-pool"),
|
|
38
40
|
/** Workspaces directory for VM work directories */
|
|
@@ -41,16 +43,24 @@ var runnerPaths = {
|
|
|
41
43
|
vmWorkDir: (baseDir, vmId) => path.join(baseDir, "workspaces", `${VM_WORKSPACE_PREFIX}${vmId}`),
|
|
42
44
|
/** Runner status file */
|
|
43
45
|
statusFile: (baseDir) => path.join(baseDir, "status.json"),
|
|
46
|
+
/** Snapshot generation work directory */
|
|
47
|
+
snapshotWorkDir: (baseDir) => path.join(baseDir, "workspaces", ".snapshot-work"),
|
|
44
48
|
/** Check if a directory name is a VM workspace */
|
|
45
49
|
isVmWorkspace: (dirname) => dirname.startsWith(VM_WORKSPACE_PREFIX),
|
|
46
50
|
/** Extract vmId from workspace directory name */
|
|
47
51
|
extractVmId: (dirname) => createVmId(dirname.replace(VM_WORKSPACE_PREFIX, ""))
|
|
48
52
|
};
|
|
49
53
|
var vmPaths = {
|
|
50
|
-
/** Firecracker config file (used with --config-file
|
|
54
|
+
/** Firecracker config file (used with --config-file) */
|
|
51
55
|
config: (workDir) => path.join(workDir, "config.json"),
|
|
52
|
-
/** Vsock
|
|
53
|
-
|
|
56
|
+
/** Vsock directory for host-guest communication */
|
|
57
|
+
vsockDir: (workDir) => path.join(workDir, "vsock"),
|
|
58
|
+
/** Vsock UDS path for host-guest communication */
|
|
59
|
+
vsock: (workDir) => path.join(workDir, "vsock", "vsock.sock"),
|
|
60
|
+
/** Firecracker API socket (used with --api-sock) */
|
|
61
|
+
apiSock: (workDir) => path.join(workDir, "api.sock"),
|
|
62
|
+
/** Overlay filesystem for VM writes */
|
|
63
|
+
overlay: (workDir) => path.join(workDir, "overlay.ext4")
|
|
54
64
|
};
|
|
55
65
|
var tempPaths = {
|
|
56
66
|
/** Default proxy CA directory */
|
|
@@ -325,15 +335,16 @@ async function subscribeToJobs(server, group, onJob, onConnectionChange) {
|
|
|
325
335
|
};
|
|
326
336
|
}
|
|
327
337
|
|
|
338
|
+
// src/lib/executor.ts
|
|
339
|
+
import fs9 from "fs";
|
|
340
|
+
|
|
328
341
|
// src/lib/firecracker/vm.ts
|
|
329
342
|
import { spawn } from "child_process";
|
|
330
343
|
import fs4 from "fs";
|
|
331
344
|
import readline from "readline";
|
|
332
345
|
|
|
333
346
|
// src/lib/firecracker/netns-pool.ts
|
|
334
|
-
import { exec } from "child_process";
|
|
335
347
|
import path2 from "path";
|
|
336
|
-
import { promisify } from "util";
|
|
337
348
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
338
349
|
import { z as z3 } from "zod";
|
|
339
350
|
|
|
@@ -377,9 +388,25 @@ async function withFileLock(path7, fn, options) {
|
|
|
377
388
|
}
|
|
378
389
|
}
|
|
379
390
|
|
|
380
|
-
// src/lib/
|
|
391
|
+
// src/lib/utils/exec.ts
|
|
392
|
+
import { exec } from "child_process";
|
|
393
|
+
import { promisify } from "util";
|
|
381
394
|
var execAsync = promisify(exec);
|
|
382
|
-
|
|
395
|
+
async function execCommand(cmd, sudo = true) {
|
|
396
|
+
const fullCmd = sudo ? `sudo ${cmd}` : cmd;
|
|
397
|
+
try {
|
|
398
|
+
const { stdout } = await execAsync(fullCmd);
|
|
399
|
+
return stdout.trim();
|
|
400
|
+
} catch (error) {
|
|
401
|
+
const execError = error;
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Command failed: ${fullCmd}
|
|
404
|
+
${execError.stderr || execError.message}`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/lib/firecracker/netns.ts
|
|
383
410
|
var SNAPSHOT_NETWORK = {
|
|
384
411
|
/** TAP device name inside namespace (must match Firecracker config) */
|
|
385
412
|
tapName: "vm0-tap",
|
|
@@ -394,14 +421,28 @@ var SNAPSHOT_NETWORK = {
|
|
|
394
421
|
/** CIDR prefix length (for ip commands) */
|
|
395
422
|
prefixLen: 29
|
|
396
423
|
};
|
|
397
|
-
var VETH_NS = "veth0";
|
|
398
|
-
var NS_PREFIX = "vm0-ns-";
|
|
399
|
-
var VETH_PREFIX = "vm0-ve-";
|
|
400
|
-
var VETH_IP_PREFIX = "10.200";
|
|
401
424
|
function generateSnapshotNetworkBootArgs() {
|
|
402
425
|
const { guestIp, gatewayIp, netmask } = SNAPSHOT_NETWORK;
|
|
403
426
|
return `ip=${guestIp}::${gatewayIp}:${netmask}:vm0-guest:eth0:off`;
|
|
404
427
|
}
|
|
428
|
+
async function createNetnsWithTap(nsName, tap) {
|
|
429
|
+
await execCommand(`ip netns add ${nsName}`);
|
|
430
|
+
await execCommand(
|
|
431
|
+
`ip netns exec ${nsName} ip tuntap add ${tap.tapName} mode tap`
|
|
432
|
+
);
|
|
433
|
+
await execCommand(
|
|
434
|
+
`ip netns exec ${nsName} ip addr add ${tap.gatewayIpWithPrefix} dev ${tap.tapName}`
|
|
435
|
+
);
|
|
436
|
+
await execCommand(`ip netns exec ${nsName} ip link set ${tap.tapName} up`);
|
|
437
|
+
await execCommand(`ip netns exec ${nsName} ip link set lo up`);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// src/lib/firecracker/netns-pool.ts
|
|
441
|
+
var logger = createLogger("NetnsPool");
|
|
442
|
+
var VETH_NS = "veth0";
|
|
443
|
+
var NS_PREFIX = "vm0-ns-";
|
|
444
|
+
var VETH_PREFIX = "vm0-ve-";
|
|
445
|
+
var VETH_IP_PREFIX = "10.200";
|
|
405
446
|
var MAX_RUNNERS = 64;
|
|
406
447
|
var MAX_NAMESPACES_PER_RUNNER = 256;
|
|
407
448
|
var NamespaceEntrySchema = z3.object({
|
|
@@ -459,19 +500,6 @@ var RegistryFile = class {
|
|
|
459
500
|
});
|
|
460
501
|
}
|
|
461
502
|
};
|
|
462
|
-
async function execCommand(cmd, sudo = true) {
|
|
463
|
-
const fullCmd = sudo ? `sudo ${cmd}` : cmd;
|
|
464
|
-
try {
|
|
465
|
-
const { stdout } = await execAsync(fullCmd);
|
|
466
|
-
return stdout.trim();
|
|
467
|
-
} catch (error) {
|
|
468
|
-
const execError = error;
|
|
469
|
-
throw new Error(
|
|
470
|
-
`Command failed: ${fullCmd}
|
|
471
|
-
${execError.stderr || execError.message}`
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
503
|
async function getDefaultInterface() {
|
|
476
504
|
const result = await execCommand("ip route get 8.8.8.8", false);
|
|
477
505
|
const match = result.match(/dev\s+(\S+)/);
|
|
@@ -694,16 +722,10 @@ var NetnsPool = class _NetnsPool {
|
|
|
694
722
|
});
|
|
695
723
|
logger.log(`Creating namespace ${name}...`);
|
|
696
724
|
try {
|
|
697
|
-
await
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
);
|
|
701
|
-
await execCommand(
|
|
702
|
-
`ip netns exec ${name} ip addr add ${SNAPSHOT_NETWORK.gatewayIp}/${SNAPSHOT_NETWORK.prefixLen} dev ${SNAPSHOT_NETWORK.tapName}`
|
|
703
|
-
);
|
|
704
|
-
await execCommand(
|
|
705
|
-
`ip netns exec ${name} ip link set ${SNAPSHOT_NETWORK.tapName} up`
|
|
706
|
-
);
|
|
725
|
+
await createNetnsWithTap(name, {
|
|
726
|
+
tapName: SNAPSHOT_NETWORK.tapName,
|
|
727
|
+
gatewayIpWithPrefix: `${SNAPSHOT_NETWORK.gatewayIp}/${SNAPSHOT_NETWORK.prefixLen}`
|
|
728
|
+
});
|
|
707
729
|
await execCommand(
|
|
708
730
|
`ip link add ${vethHost} type veth peer name ${VETH_NS} netns ${name}`
|
|
709
731
|
);
|
|
@@ -711,7 +733,6 @@ var NetnsPool = class _NetnsPool {
|
|
|
711
733
|
`ip netns exec ${name} ip addr add ${vethNsIp}/30 dev ${VETH_NS}`
|
|
712
734
|
);
|
|
713
735
|
await execCommand(`ip netns exec ${name} ip link set ${VETH_NS} up`);
|
|
714
|
-
await execCommand(`ip netns exec ${name} ip link set lo up`);
|
|
715
736
|
await execCommand(`ip addr add ${vethHostIp}/30 dev ${vethHost}`);
|
|
716
737
|
await execCommand(`ip link set ${vethHost} up`);
|
|
717
738
|
await execCommand(
|
|
@@ -1068,6 +1089,36 @@ var FirecrackerClient = class {
|
|
|
1068
1089
|
constructor(socketPath) {
|
|
1069
1090
|
this.socketPath = socketPath;
|
|
1070
1091
|
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Configure machine settings (vCPUs, memory)
|
|
1094
|
+
*/
|
|
1095
|
+
async configureMachine(config) {
|
|
1096
|
+
await this.put("/machine-config", config);
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Configure boot source (kernel, boot args)
|
|
1100
|
+
*/
|
|
1101
|
+
async configureBootSource(config) {
|
|
1102
|
+
await this.put("/boot-source", config);
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Configure network interface
|
|
1106
|
+
*/
|
|
1107
|
+
async configureNetworkInterface(config) {
|
|
1108
|
+
await this.put(`/network-interfaces/${config.iface_id}`, config);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Configure vsock device
|
|
1112
|
+
*/
|
|
1113
|
+
async configureVsock(config) {
|
|
1114
|
+
await this.put("/vsock", config);
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Start the VM instance
|
|
1118
|
+
*/
|
|
1119
|
+
async startInstance() {
|
|
1120
|
+
await this.put("/actions", { action_type: "InstanceStart" });
|
|
1121
|
+
}
|
|
1071
1122
|
/**
|
|
1072
1123
|
* Pause the VM
|
|
1073
1124
|
*
|
|
@@ -1164,17 +1215,27 @@ var FirecrackerClient = class {
|
|
|
1164
1215
|
*/
|
|
1165
1216
|
request(method, path7, body, timeoutMs = 3e4) {
|
|
1166
1217
|
return new Promise((resolve, reject) => {
|
|
1218
|
+
const bodyStr = body !== void 0 ? JSON.stringify(body) : void 0;
|
|
1219
|
+
const headers = {
|
|
1220
|
+
Accept: "application/json",
|
|
1221
|
+
// Disable keep-alive to prevent pipelining issues
|
|
1222
|
+
Connection: "close"
|
|
1223
|
+
};
|
|
1224
|
+
if (bodyStr !== void 0) {
|
|
1225
|
+
headers["Content-Type"] = "application/json";
|
|
1226
|
+
headers["Content-Length"] = Buffer.byteLength(bodyStr);
|
|
1227
|
+
}
|
|
1167
1228
|
const options = {
|
|
1168
1229
|
socketPath: this.socketPath,
|
|
1169
|
-
path:
|
|
1230
|
+
path: path7,
|
|
1170
1231
|
method,
|
|
1171
|
-
headers
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1232
|
+
headers,
|
|
1233
|
+
timeout: timeoutMs,
|
|
1234
|
+
// Disable agent to ensure fresh connection for each request
|
|
1235
|
+
// Firecracker's single-threaded API can have issues with pipelined requests
|
|
1236
|
+
agent: false
|
|
1176
1237
|
};
|
|
1177
|
-
logger3.log(`${method} ${path7}${
|
|
1238
|
+
logger3.log(`${method} ${path7}${bodyStr ? " " + bodyStr : ""}`);
|
|
1178
1239
|
const req = http.request(options, (res) => {
|
|
1179
1240
|
let data = "";
|
|
1180
1241
|
res.on("data", (chunk) => {
|
|
@@ -1204,8 +1265,8 @@ var FirecrackerClient = class {
|
|
|
1204
1265
|
req.on("error", (err) => {
|
|
1205
1266
|
reject(err);
|
|
1206
1267
|
});
|
|
1207
|
-
if (
|
|
1208
|
-
req.write(
|
|
1268
|
+
if (bodyStr !== void 0) {
|
|
1269
|
+
req.write(bodyStr);
|
|
1209
1270
|
}
|
|
1210
1271
|
req.end();
|
|
1211
1272
|
});
|
|
@@ -1218,6 +1279,58 @@ var FirecrackerClient = class {
|
|
|
1218
1279
|
}
|
|
1219
1280
|
};
|
|
1220
1281
|
|
|
1282
|
+
// src/lib/firecracker/config.ts
|
|
1283
|
+
function buildBootArgs() {
|
|
1284
|
+
const networkBootArgs = generateSnapshotNetworkBootArgs();
|
|
1285
|
+
return `console=ttyS0 reboot=k panic=1 pci=off nomodules random.trust_cpu=on quiet loglevel=0 nokaslr audit=0 numa=off mitigations=off noresume init=/sbin/vm-init ${networkBootArgs}`;
|
|
1286
|
+
}
|
|
1287
|
+
function buildFirecrackerConfig(params) {
|
|
1288
|
+
const bootArgs = buildBootArgs();
|
|
1289
|
+
return {
|
|
1290
|
+
"boot-source": {
|
|
1291
|
+
kernel_image_path: params.kernelPath,
|
|
1292
|
+
boot_args: bootArgs
|
|
1293
|
+
},
|
|
1294
|
+
drives: [
|
|
1295
|
+
// Base drive (squashfs, read-only, shared across VMs)
|
|
1296
|
+
// Mounted as /dev/vda inside the VM
|
|
1297
|
+
{
|
|
1298
|
+
drive_id: "rootfs",
|
|
1299
|
+
path_on_host: params.rootfsPath,
|
|
1300
|
+
is_root_device: true,
|
|
1301
|
+
is_read_only: true
|
|
1302
|
+
},
|
|
1303
|
+
// Overlay drive (ext4, read-write, per-VM)
|
|
1304
|
+
// Mounted as /dev/vdb inside the VM
|
|
1305
|
+
// The vm-init script combines these using overlayfs
|
|
1306
|
+
{
|
|
1307
|
+
drive_id: "overlay",
|
|
1308
|
+
path_on_host: params.overlayPath,
|
|
1309
|
+
is_root_device: false,
|
|
1310
|
+
is_read_only: false
|
|
1311
|
+
}
|
|
1312
|
+
],
|
|
1313
|
+
"machine-config": {
|
|
1314
|
+
vcpu_count: params.vcpus,
|
|
1315
|
+
mem_size_mib: params.memoryMb
|
|
1316
|
+
},
|
|
1317
|
+
"network-interfaces": [
|
|
1318
|
+
{
|
|
1319
|
+
// Network interface uses fixed config from SNAPSHOT_NETWORK
|
|
1320
|
+
// TAP device is inside the namespace, created by netns-pool
|
|
1321
|
+
iface_id: "eth0",
|
|
1322
|
+
guest_mac: SNAPSHOT_NETWORK.guestMac,
|
|
1323
|
+
host_dev_name: SNAPSHOT_NETWORK.tapName
|
|
1324
|
+
}
|
|
1325
|
+
],
|
|
1326
|
+
// Guest CID 3 is the standard guest identifier (CID 0=hypervisor, 1=local, 2=host)
|
|
1327
|
+
vsock: {
|
|
1328
|
+
guest_cid: 3,
|
|
1329
|
+
uds_path: params.vsockPath
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1221
1334
|
// src/lib/firecracker/vm.ts
|
|
1222
1335
|
var logger4 = createLogger("VM");
|
|
1223
1336
|
var FirecrackerVM = class {
|
|
@@ -1282,7 +1395,6 @@ var FirecrackerVM = class {
|
|
|
1282
1395
|
throw new Error(`Cannot start VM in state: ${this.state}`);
|
|
1283
1396
|
}
|
|
1284
1397
|
try {
|
|
1285
|
-
fs4.mkdirSync(this.workDir, { recursive: true });
|
|
1286
1398
|
logger4.log(`[VM ${this.config.vmId}] Acquiring resources...`);
|
|
1287
1399
|
const results = await Promise.allSettled([
|
|
1288
1400
|
acquireOverlay(),
|
|
@@ -1458,74 +1570,23 @@ var FirecrackerVM = class {
|
|
|
1458
1570
|
}
|
|
1459
1571
|
/**
|
|
1460
1572
|
* Build Firecracker configuration object
|
|
1461
|
-
*
|
|
1462
|
-
* Creates the JSON configuration for Firecracker's --config-file option.
|
|
1463
|
-
* Boot args:
|
|
1464
|
-
* - console=ttyS0: serial console output
|
|
1465
|
-
* - reboot=k: use keyboard controller for reboot
|
|
1466
|
-
* - panic=1: reboot after 1 second on kernel panic
|
|
1467
|
-
* - pci=off: disable PCI bus (not needed in microVM)
|
|
1468
|
-
* - nomodules: skip module loading (not needed in microVM)
|
|
1469
|
-
* - random.trust_cpu=on: trust CPU RNG, skip entropy wait
|
|
1470
|
-
* - quiet loglevel=0: minimize kernel log output
|
|
1471
|
-
* - nokaslr: disable kernel address space randomization
|
|
1472
|
-
* - audit=0: disable kernel auditing
|
|
1473
|
-
* - numa=off: disable NUMA (single node)
|
|
1474
|
-
* - mitigations=off: disable CPU vulnerability mitigations
|
|
1475
|
-
* - noresume: skip hibernation resume check
|
|
1476
|
-
* - init=/sbin/vm-init: use vm-init (Rust binary) for filesystem setup and vsock-agent
|
|
1477
|
-
* - ip=...: network configuration (fixed IPs from SNAPSHOT_NETWORK)
|
|
1478
1573
|
*/
|
|
1479
1574
|
buildConfig() {
|
|
1480
1575
|
if (!this.netns || !this.vmOverlayPath) {
|
|
1481
1576
|
throw new Error("VM not properly initialized");
|
|
1482
1577
|
}
|
|
1483
|
-
const
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
drive_id: "rootfs",
|
|
1496
|
-
path_on_host: this.config.rootfsPath,
|
|
1497
|
-
is_root_device: true,
|
|
1498
|
-
is_read_only: true
|
|
1499
|
-
},
|
|
1500
|
-
// Overlay drive (ext4, read-write, per-VM)
|
|
1501
|
-
// Mounted as /dev/vdb inside the VM
|
|
1502
|
-
// The vm-init script combines these using overlayfs
|
|
1503
|
-
{
|
|
1504
|
-
drive_id: "overlay",
|
|
1505
|
-
path_on_host: this.vmOverlayPath,
|
|
1506
|
-
is_root_device: false,
|
|
1507
|
-
is_read_only: false
|
|
1508
|
-
}
|
|
1509
|
-
],
|
|
1510
|
-
"machine-config": {
|
|
1511
|
-
vcpu_count: this.config.vcpus,
|
|
1512
|
-
mem_size_mib: this.config.memoryMb
|
|
1513
|
-
},
|
|
1514
|
-
"network-interfaces": [
|
|
1515
|
-
{
|
|
1516
|
-
// Network interface uses fixed config from SNAPSHOT_NETWORK
|
|
1517
|
-
// TAP device is inside the namespace, created by netns-pool
|
|
1518
|
-
iface_id: "eth0",
|
|
1519
|
-
guest_mac: SNAPSHOT_NETWORK.guestMac,
|
|
1520
|
-
host_dev_name: SNAPSHOT_NETWORK.tapName
|
|
1521
|
-
}
|
|
1522
|
-
],
|
|
1523
|
-
// Guest CID 3 is the standard guest identifier (CID 0=hypervisor, 1=local, 2=host)
|
|
1524
|
-
vsock: {
|
|
1525
|
-
guest_cid: 3,
|
|
1526
|
-
uds_path: this.vsockPath
|
|
1527
|
-
}
|
|
1528
|
-
};
|
|
1578
|
+
const config = buildFirecrackerConfig({
|
|
1579
|
+
kernelPath: this.config.kernelPath,
|
|
1580
|
+
rootfsPath: this.config.rootfsPath,
|
|
1581
|
+
overlayPath: this.vmOverlayPath,
|
|
1582
|
+
vsockPath: this.vsockPath,
|
|
1583
|
+
vcpus: this.config.vcpus,
|
|
1584
|
+
memoryMb: this.config.memoryMb
|
|
1585
|
+
});
|
|
1586
|
+
logger4.log(
|
|
1587
|
+
`[VM ${this.config.vmId}] Boot args: ${config["boot-source"].boot_args}`
|
|
1588
|
+
);
|
|
1589
|
+
return config;
|
|
1529
1590
|
}
|
|
1530
1591
|
/**
|
|
1531
1592
|
* Stop the VM
|
|
@@ -7653,6 +7714,7 @@ var modelProviderTypeSchema = z18.enum([
|
|
|
7653
7714
|
"openrouter-api-key",
|
|
7654
7715
|
"moonshot-api-key",
|
|
7655
7716
|
"minimax-api-key",
|
|
7717
|
+
"deepseek-api-key",
|
|
7656
7718
|
"aws-bedrock"
|
|
7657
7719
|
]);
|
|
7658
7720
|
var modelProviderFrameworkSchema = z18.enum(["claude-code", "codex"]);
|
|
@@ -10026,6 +10088,13 @@ async function executeJob(context, config, options = {}) {
|
|
|
10026
10088
|
firecrackerBinary: config.firecracker.binary,
|
|
10027
10089
|
workDir: runnerPaths.vmWorkDir(config.base_dir, vmId)
|
|
10028
10090
|
};
|
|
10091
|
+
fs9.mkdirSync(vmConfig.workDir, { recursive: true });
|
|
10092
|
+
fs9.mkdirSync(vmPaths.vsockDir(vmConfig.workDir), { recursive: true });
|
|
10093
|
+
const vsockPath = vmPaths.vsock(vmConfig.workDir);
|
|
10094
|
+
vsockClient = new VsockClient(vsockPath);
|
|
10095
|
+
const guest = vsockClient;
|
|
10096
|
+
logger9.log(`Starting vsock listener: ${vsockPath}`);
|
|
10097
|
+
const guestConnectionPromise = guest.waitForGuestConnection(3e4);
|
|
10029
10098
|
logger9.log(`Creating VM ${vmId}...`);
|
|
10030
10099
|
vm = new FirecrackerVM(vmConfig);
|
|
10031
10100
|
await withSandboxTiming("vm_create", () => vm.start());
|
|
@@ -10037,10 +10106,6 @@ async function executeJob(context, config, options = {}) {
|
|
|
10037
10106
|
logger9.log(
|
|
10038
10107
|
`VM ${vmId} started, guest IP: ${guestIp}, veth NS IP: ${vethNsIp}`
|
|
10039
10108
|
);
|
|
10040
|
-
const vsockPath = vm.getVsockPath();
|
|
10041
|
-
vsockClient = new VsockClient(vsockPath);
|
|
10042
|
-
const guest = vsockClient;
|
|
10043
|
-
logger9.log(`Using vsock for guest communication: ${vsockPath}`);
|
|
10044
10109
|
const envJson = JSON.stringify(
|
|
10045
10110
|
buildEnvironmentVariables(context, config.server.url)
|
|
10046
10111
|
);
|
|
@@ -10058,10 +10123,7 @@ async function executeJob(context, config, options = {}) {
|
|
|
10058
10123
|
});
|
|
10059
10124
|
}
|
|
10060
10125
|
logger9.log(`Waiting for guest connection...`);
|
|
10061
|
-
await withSandboxTiming(
|
|
10062
|
-
"guest_wait",
|
|
10063
|
-
() => guest.waitForGuestConnection(3e4)
|
|
10064
|
-
);
|
|
10126
|
+
await withSandboxTiming("guest_wait", () => guestConnectionPromise);
|
|
10065
10127
|
logger9.log(`Guest client ready`);
|
|
10066
10128
|
if (context.storageManifest) {
|
|
10067
10129
|
await withSandboxTiming(
|
|
@@ -10264,7 +10326,7 @@ async function isPortInUse(port) {
|
|
|
10264
10326
|
}
|
|
10265
10327
|
|
|
10266
10328
|
// src/lib/runner/runner-lock.ts
|
|
10267
|
-
import
|
|
10329
|
+
import fs10 from "fs";
|
|
10268
10330
|
import path5 from "path";
|
|
10269
10331
|
var logger11 = createLogger("RunnerLock");
|
|
10270
10332
|
var DEFAULT_PID_FILE = runtimePaths.runnerPid;
|
|
@@ -10283,9 +10345,9 @@ function isProcessRunning(pid) {
|
|
|
10283
10345
|
function acquireRunnerLock(options = {}) {
|
|
10284
10346
|
const pidFile = options.pidFile ?? DEFAULT_PID_FILE;
|
|
10285
10347
|
const runDir = path5.dirname(pidFile);
|
|
10286
|
-
|
|
10287
|
-
if (
|
|
10288
|
-
const pidStr =
|
|
10348
|
+
fs10.mkdirSync(runDir, { recursive: true });
|
|
10349
|
+
if (fs10.existsSync(pidFile)) {
|
|
10350
|
+
const pidStr = fs10.readFileSync(pidFile, "utf-8").trim();
|
|
10289
10351
|
const pid = parseInt(pidStr, 10);
|
|
10290
10352
|
if (!isNaN(pid) && isProcessRunning(pid)) {
|
|
10291
10353
|
logger11.error(`Error: Another runner is already running (PID ${pid})`);
|
|
@@ -10297,16 +10359,16 @@ function acquireRunnerLock(options = {}) {
|
|
|
10297
10359
|
} else {
|
|
10298
10360
|
logger11.log(`Cleaning up stale PID file (PID ${pid} not running)`);
|
|
10299
10361
|
}
|
|
10300
|
-
|
|
10362
|
+
fs10.unlinkSync(pidFile);
|
|
10301
10363
|
}
|
|
10302
|
-
|
|
10364
|
+
fs10.writeFileSync(pidFile, process.pid.toString());
|
|
10303
10365
|
currentPidFile = pidFile;
|
|
10304
10366
|
logger11.log(`Runner lock acquired (PID ${process.pid})`);
|
|
10305
10367
|
}
|
|
10306
10368
|
function releaseRunnerLock() {
|
|
10307
10369
|
const pidFile = currentPidFile ?? DEFAULT_PID_FILE;
|
|
10308
|
-
if (
|
|
10309
|
-
|
|
10370
|
+
if (fs10.existsSync(pidFile)) {
|
|
10371
|
+
fs10.unlinkSync(pidFile);
|
|
10310
10372
|
logger11.log("Runner lock released");
|
|
10311
10373
|
}
|
|
10312
10374
|
currentPidFile = null;
|
|
@@ -11178,7 +11240,7 @@ var benchmarkCommand = new Command4("benchmark").description(
|
|
|
11178
11240
|
});
|
|
11179
11241
|
|
|
11180
11242
|
// src/index.ts
|
|
11181
|
-
var version = true ? "3.11.
|
|
11243
|
+
var version = true ? "3.11.2" : "0.1.0";
|
|
11182
11244
|
program.name("vm0-runner").version(version).description("Self-hosted runner for VM0 agents");
|
|
11183
11245
|
program.addCommand(startCommand);
|
|
11184
11246
|
program.addCommand(doctorCommand);
|