204 lines
6.3 KiB
TypeScript
204 lines
6.3 KiB
TypeScript
import { assert, describe } from 'vitest'
|
|
|
|
import { sandboxTest, isDebug } from '../setup.js'
|
|
|
|
sandboxTest.skipIf(isDebug)(
|
|
'pause and resume a sandbox',
|
|
async ({ sandbox }) => {
|
|
assert.isTrue(await sandbox.isRunning())
|
|
|
|
await sandbox.pause()
|
|
|
|
assert.isFalse(await sandbox.isRunning())
|
|
|
|
const resumedSandbox = await sandbox.connect()
|
|
assert.equal(resumedSandbox.sandboxId, sandbox.sandboxId)
|
|
|
|
assert.isTrue(await sandbox.isRunning())
|
|
}
|
|
)
|
|
|
|
describe('pause and resume with env vars', () => {
|
|
sandboxTest.scoped({
|
|
sandboxOpts: {
|
|
envs: { TEST_VAR: 'sfisback' },
|
|
},
|
|
})
|
|
|
|
sandboxTest.skipIf(isDebug)(
|
|
'pause and resume a sandbox with env vars',
|
|
async ({ sandbox }) => {
|
|
// Environment variables of a process exist at runtime, and are not stored in some file or so.
|
|
// They are stored in the process's own memory
|
|
const cmd = await sandbox.commands.run('echo "$TEST_VAR"')
|
|
|
|
assert.equal(cmd.exitCode, 0)
|
|
assert.equal(cmd.stdout.trim(), 'sfisback')
|
|
|
|
await sandbox.pause()
|
|
|
|
assert.isFalse(await sandbox.isRunning())
|
|
|
|
const resumedSandbox = await sandbox.connect()
|
|
assert.isTrue(await sandbox.isRunning())
|
|
assert.isTrue(await resumedSandbox.isRunning())
|
|
assert.equal(resumedSandbox.sandboxId, sandbox.sandboxId)
|
|
|
|
const cmd2 = await sandbox.commands.run('echo "$TEST_VAR"')
|
|
|
|
assert.equal(cmd2.exitCode, 0)
|
|
assert.equal(cmd2.stdout.trim(), 'sfisback')
|
|
}
|
|
)
|
|
})
|
|
|
|
sandboxTest.skipIf(isDebug)(
|
|
'pause and resume a sandbox with file',
|
|
async ({ sandbox }) => {
|
|
const filename = 'test_snapshot.txt'
|
|
const content = 'This is a snapshot test file.'
|
|
|
|
const info = await sandbox.files.write(filename, content)
|
|
assert.equal(info.name, filename)
|
|
assert.equal(info.type, 'file')
|
|
assert.equal(info.path, `/home/user/${filename}`)
|
|
|
|
const exists = await sandbox.files.exists(filename)
|
|
assert.isTrue(exists)
|
|
const readContent = await sandbox.files.read(filename)
|
|
assert.equal(readContent, content)
|
|
|
|
await sandbox.pause()
|
|
assert.isFalse(await sandbox.isRunning())
|
|
|
|
await sandbox.connect()
|
|
assert.isTrue(await sandbox.isRunning())
|
|
|
|
const exists2 = await sandbox.files.exists(filename)
|
|
assert.isTrue(exists2)
|
|
const readContent2 = await sandbox.files.read(filename)
|
|
assert.equal(readContent2, content)
|
|
}
|
|
)
|
|
|
|
sandboxTest.skipIf(isDebug)(
|
|
'pause and resume a sandbox with ongoing long running process',
|
|
async ({ sandbox }) => {
|
|
const cmd = await sandbox.commands.run('sleep 3600', { background: true })
|
|
const expectedPid = cmd.pid
|
|
|
|
await sandbox.pause()
|
|
assert.isFalse(await sandbox.isRunning())
|
|
|
|
await sandbox.connect()
|
|
assert.isTrue(await sandbox.isRunning())
|
|
|
|
// First check that the command is in list
|
|
const list = await sandbox.commands.list()
|
|
assert.isTrue(list.some((c) => c.pid === expectedPid))
|
|
|
|
// Make sure we can connect to it
|
|
const processInfo = await sandbox.commands.connect(expectedPid)
|
|
|
|
assert.isObject(processInfo)
|
|
assert.equal(processInfo.pid, expectedPid)
|
|
}
|
|
)
|
|
|
|
sandboxTest.skipIf(isDebug)(
|
|
'pause and resume a sandbox with completed long running process',
|
|
async ({ sandbox }) => {
|
|
const filename = 'test_long_running.txt'
|
|
|
|
await sandbox.commands.run(
|
|
`sleep 2 && echo "done" > /home/user/${filename}`,
|
|
{
|
|
background: true,
|
|
}
|
|
)
|
|
|
|
// the file should not exist before 2 seconds have elapsed
|
|
const exists = await sandbox.files.exists(filename)
|
|
assert.isFalse(exists)
|
|
|
|
await sandbox.pause()
|
|
assert.isFalse(await sandbox.isRunning())
|
|
|
|
await sandbox.connect()
|
|
assert.isTrue(await sandbox.isRunning())
|
|
|
|
// the file should be created after more than 2 seconds have elapsed
|
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
|
|
const exists2 = await sandbox.files.exists(filename)
|
|
assert.isTrue(exists2)
|
|
const readContent2 = await sandbox.files.read(filename)
|
|
assert.equal(readContent2.trim(), 'done')
|
|
}
|
|
)
|
|
|
|
sandboxTest.skipIf(isDebug)(
|
|
'pause and resume a sandbox with http server',
|
|
async ({ sandbox }) => {
|
|
await sandbox.commands.run('python3 -m http.server 8000', {
|
|
background: true,
|
|
})
|
|
|
|
let url = await sandbox.getHost(8000)
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 5000))
|
|
|
|
const response1 = await fetch(`https://${url}`)
|
|
assert.equal(response1.status, 200)
|
|
|
|
await sandbox.pause()
|
|
assert.isFalse(await sandbox.isRunning())
|
|
|
|
await sandbox.connect()
|
|
assert.isTrue(await sandbox.isRunning())
|
|
|
|
url = await sandbox.getHost(8000)
|
|
const response2 = await fetch(`https://${url}`)
|
|
assert.equal(response2.status, 200)
|
|
}
|
|
)
|
|
|
|
sandboxTest.skipIf(isDebug)(
|
|
'filesystem-only pause reboots on resume but keeps the filesystem',
|
|
async ({ sandbox }) => {
|
|
// Absolute path: a cold boot may not restore the template's default
|
|
// user/cwd, so a relative path could resolve differently after resume.
|
|
const filename = '/home/user/fs_only_snapshot.txt'
|
|
const content = 'This is a filesystem-only snapshot test file.'
|
|
|
|
await sandbox.files.write(filename, content)
|
|
|
|
// Kernel boot id before the pause; it changes only across a real (cold) boot.
|
|
const bootBefore = (
|
|
await sandbox.files.read('/proc/sys/kernel/random/boot_id')
|
|
).trim()
|
|
|
|
// Filesystem-only snapshot: no memory is captured, so resuming cold-boots.
|
|
await sandbox.pause({ keepMemory: false })
|
|
assert.isFalse(await sandbox.isRunning())
|
|
|
|
// Resume the paused sandbox; a filesystem-only pause keeps no memory, so
|
|
// connect() cold-boots (reboots) it. connect() returns the same handle, and
|
|
// its credentials stay valid across the resume (the backend re-binds the
|
|
// same envd access token on the cold boot).
|
|
const resumedSandbox = await sandbox.connect()
|
|
assert.equal(resumedSandbox.sandboxId, sandbox.sandboxId)
|
|
assert.isTrue(await resumedSandbox.isRunning())
|
|
|
|
// The filesystem survives the cold boot.
|
|
assert.isTrue(await resumedSandbox.files.exists(filename))
|
|
assert.equal(await resumedSandbox.files.read(filename), content)
|
|
|
|
// A fresh boot id proves the guest rebooted rather than restoring memory.
|
|
const bootAfter = (
|
|
await resumedSandbox.files.read('/proc/sys/kernel/random/boot_id')
|
|
).trim()
|
|
assert.notEqual(bootAfter, bootBefore)
|
|
}
|
|
)
|