Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/codev-sandbox/image/guest/codev-init
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ mount -t tmpfs tmpfs /tmp
mount -t virtiofs workspace /workspace
mount -t virtiofs bundle /opt/codev
mount -t virtiofs config /codev-config
# Extra host folders arrive as additional virtiofs tags listed in mounts.json,
# which the host rewrites on every start. Tags and guest paths are sanitized
# host-side (no spaces or quotes); a failed mount is logged and skipped so one
# bad share cannot brick the boot.
if [ -f /codev-config/mounts.json ]; then
node -e '
for (const m of require("/codev-config/mounts.json").mounts ?? []) console.log(m.tag, m.path)
' | while read -r tag path; do
mkdir -p "$path" && mount -t virtiofs "$tag" "$path" || echo "codev-init: mount $tag at $path failed" >&2
done
fi
ip link set lo up

[ -f /etc/codev-guest.env ] && . /etc/codev-guest.env
Expand Down
46 changes: 44 additions & 2 deletions packages/codev-sandbox/script/e2e-vz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const work = await mkdtemp(join(tmpdir(), "codev-vz-e2e-"))
const projectDir = argValue("--project") ?? join(work, "project")
await mkdir(projectDir, { recursive: true })
await writeFile(join(projectDir, "hello.txt"), "hello from the host project\n")
// A second host folder mounted on top of the project dir, to prove the
// mounts.json path: the guest must see it at /mnt/extra.
const extraDir = join(work, "extra")
await mkdir(extraDir, { recursive: true })
await writeFile(join(extraDir, "marker.txt"), "extra-mount-ok\n")

const payload = Bun.file(image)
const hash = createHash("sha256")
Expand Down Expand Up @@ -55,7 +60,13 @@ const provider = createVzProvider({
})

console.log("availability:", await provider.availability())
const config: SandboxConfig = { id: "sandbox:e2e", name: "e2e", backend: "vz", projectDir }
const config: SandboxConfig = {
id: "sandbox:e2e",
name: "e2e",
backend: "vz",
projectDir,
mounts: [{ path: extraDir }],
}

console.time("create")
await provider.create(config)
Expand Down Expand Up @@ -106,6 +117,37 @@ const brokerOk = await (async () => {
})()
console.log(`broker: ${brokerOk ? "OK" : "FAILED"}`)

// Extra mount: the guest reads the marker from /mnt/extra (mounted by
// codev-init from mounts.json) and writes proof both into the workspace and
// back into the extra mount itself, exercising read and write.
console.log(`guest mounts: ${JSON.stringify(sandbox.mounts)}`)
await fetch(`${sandbox.url}/api/pty`, {
method: "POST",
headers: { authorization: auth, "content-type": "application/json" },
body: JSON.stringify({
command: "/bin/sh",
args: [
"-c",
"cat /mnt/extra/marker.txt > /workspace/mount-probe.txt 2>&1; echo written-from-guest > /mnt/extra/guest-write.txt 2>/dev/null",
],
cwd: "/workspace",
}),
}).catch(() => null)
const mountOk = await (async () => {
for (let attempt = 0; attempt < 30; attempt++) {
const read = await Bun.file(join(projectDir, "mount-probe.txt"))
.text()
.catch(() => "")
const written = await Bun.file(join(extraDir, "guest-write.txt"))
.text()
.catch(() => "")
if (read.trim() === "extra-mount-ok" && written.trim() === "written-from-guest") return true
await new Promise((resolve) => setTimeout(resolve, 500))
}
return false
})()
console.log(`extra mount: ${mountOk ? "OK" : "FAILED"}`)

if (hold) {
console.log("holding the VM up; Ctrl-C to stop")
await new Promise(() => {})
Expand All @@ -117,7 +159,7 @@ await Promise.race([exited, new Promise((resolve) => setTimeout(resolve, 10_000)
await provider.destroy(config)
server.stop(true)
await rm(work, { recursive: true, force: true })
if (!healthy || !pathsOk || !brokerOk) process.exit(1)
if (!healthy || !pathsOk || !brokerOk || !mountOk) process.exit(1)
console.log("e2e OK")
process.exit(0)

Expand Down
Loading