Extract shared VM lifecycle helpers into firecracker-vm.ts
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
@@ -12,18 +11,18 @@ import { join } from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { CONFIG } from "./config.js";
|
||||
import {
|
||||
ensureBridge,
|
||||
ensureNat,
|
||||
allocateIp,
|
||||
releaseIp,
|
||||
createTap,
|
||||
deleteTap,
|
||||
macFromOctet,
|
||||
applyNetworkPolicy,
|
||||
removeNetworkPolicy,
|
||||
type NetworkPolicy,
|
||||
} from "./network.js";
|
||||
import * as api from "./firecracker-api.js";
|
||||
import {
|
||||
setupNetwork,
|
||||
spawnFirecracker,
|
||||
bootVM,
|
||||
} from "./firecracker-vm.js";
|
||||
|
||||
export interface AgentInfo {
|
||||
name: string;
|
||||
@@ -201,24 +200,6 @@ function ensureWorkspace(agentName: string): string {
|
||||
return imgPath;
|
||||
}
|
||||
|
||||
function waitForSocket(socketPath: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const deadline = Date.now() + 5_000;
|
||||
const check = () => {
|
||||
if (existsSync(socketPath)) {
|
||||
setTimeout(resolve, 200);
|
||||
return;
|
||||
}
|
||||
if (Date.now() > deadline) {
|
||||
reject(new Error("Firecracker socket did not appear"));
|
||||
return;
|
||||
}
|
||||
setTimeout(check, 50);
|
||||
};
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
export async function startAgent(
|
||||
templateName: string,
|
||||
overrides?: { name?: string; model?: string }
|
||||
@@ -266,46 +247,18 @@ export async function startAgent(
|
||||
const workspacePath = ensureWorkspace(name);
|
||||
|
||||
// Setup network
|
||||
ensureBridge();
|
||||
ensureNat();
|
||||
deleteTap(tapDevice); // clean stale tap from previous run
|
||||
createTap(tapDevice);
|
||||
setupNetwork(tapDevice);
|
||||
|
||||
// Boot VM
|
||||
const proc = spawn(
|
||||
CONFIG.firecrackerBin,
|
||||
["--api-sock", socketPath],
|
||||
{ stdio: "pipe", detached: true }
|
||||
);
|
||||
proc.unref();
|
||||
|
||||
await waitForSocket(socketPath);
|
||||
|
||||
const bootArgs = [
|
||||
"console=ttyS0",
|
||||
"reboot=k",
|
||||
"panic=1",
|
||||
"pci=off",
|
||||
"root=/dev/vda",
|
||||
"rw",
|
||||
`ip=${ip}::${CONFIG.bridge.gateway}:${CONFIG.bridge.netmask}::eth0:off`,
|
||||
].join(" ");
|
||||
|
||||
await api.putBootSource(socketPath, CONFIG.kernelPath, bootArgs);
|
||||
await api.putDrive(socketPath, "rootfs", rootfsPath);
|
||||
await api.putDrive(socketPath, "workspace", workspacePath, false, false);
|
||||
await api.putNetworkInterface(
|
||||
const proc = await spawnFirecracker(socketPath, { detached: true });
|
||||
await bootVM({
|
||||
socketPath,
|
||||
"eth0",
|
||||
rootfsPath,
|
||||
extraDrives: [{ id: "workspace", path: workspacePath }],
|
||||
tapDevice,
|
||||
macFromOctet(octet)
|
||||
);
|
||||
await api.putMachineConfig(
|
||||
socketPath,
|
||||
CONFIG.vm.vcpuCount,
|
||||
CONFIG.vm.memSizeMib
|
||||
);
|
||||
await api.startInstance(socketPath);
|
||||
ip,
|
||||
octet,
|
||||
});
|
||||
|
||||
// Apply network policy
|
||||
const networkPolicy: NetworkPolicy = template.network ?? "full";
|
||||
|
||||
Reference in New Issue
Block a user