92 lines
3.0 KiB
TypeScript
92 lines
3.0 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { BaseImageManager, SmartVM } from '../ts/index.js';
|
|
import type { TBaseImagePreset, TFirecrackerArch } from '../ts/index.js';
|
|
|
|
const integrationEnabled = ['1', 'true', 'yes'].includes(
|
|
(process.env.SMARTVM_RUN_INTEGRATION || '').toLowerCase(),
|
|
);
|
|
|
|
function getHostArch(): TFirecrackerArch {
|
|
return process.arch === 'arm64' ? 'aarch64' : 'x86_64';
|
|
}
|
|
|
|
async function assertHostReady(): Promise<void> {
|
|
if (process.platform !== 'linux') {
|
|
throw new Error('Firecracker integration tests require Linux');
|
|
}
|
|
await fs.promises.access('/dev/kvm', fs.constants.R_OK | fs.constants.W_OK);
|
|
}
|
|
|
|
tap.test('SmartVM integration - boots a Firecracker CI base image when explicitly enabled', async () => {
|
|
if (!integrationEnabled) {
|
|
console.log('Skipping SmartVM integration test. Set SMARTVM_RUN_INTEGRATION=true to enable it.');
|
|
return;
|
|
}
|
|
|
|
await assertHostReady();
|
|
|
|
const arch = (process.env.SMARTVM_ARCH as TFirecrackerArch | undefined) || getHostArch();
|
|
const preset = (process.env.SMARTVM_BASE_IMAGE_PRESET as TBaseImagePreset | undefined) || 'latest';
|
|
const maxStoredBaseImages = process.env.SMARTVM_MAX_STORED_BASE_IMAGES
|
|
? Number(process.env.SMARTVM_MAX_STORED_BASE_IMAGES)
|
|
: undefined;
|
|
const baseImageManager = new BaseImageManager({
|
|
arch,
|
|
cacheDir: process.env.SMARTVM_BASE_IMAGE_CACHE_DIR,
|
|
maxStoredBaseImages,
|
|
hostedManifestUrl: process.env.SMARTVM_BASE_IMAGE_MANIFEST_URL,
|
|
hostedManifestPath: process.env.SMARTVM_BASE_IMAGE_MANIFEST_PATH,
|
|
});
|
|
const baseImage = await baseImageManager.ensureBaseImage({
|
|
preset,
|
|
manifestUrl: process.env.SMARTVM_BASE_IMAGE_MANIFEST_URL,
|
|
manifestPath: process.env.SMARTVM_BASE_IMAGE_MANIFEST_PATH,
|
|
});
|
|
|
|
const smartvm = new SmartVM({
|
|
arch,
|
|
dataDir: process.env.SMARTVM_INTEGRATION_DATA_DIR || path.join(os.tmpdir(), '.smartvm-integration'),
|
|
firecrackerVersion: process.env.SMARTVM_FIRECRACKER_VERSION || baseImage.firecrackerVersion,
|
|
baseImageCacheDir: process.env.SMARTVM_BASE_IMAGE_CACHE_DIR,
|
|
maxStoredBaseImages,
|
|
baseImageManifestUrl: process.env.SMARTVM_BASE_IMAGE_MANIFEST_URL,
|
|
baseImageManifestPath: process.env.SMARTVM_BASE_IMAGE_MANIFEST_PATH,
|
|
});
|
|
const vm = await smartvm.createVM({
|
|
id: `smartvm-it-${Date.now()}`,
|
|
bootSource: {
|
|
kernelImagePath: baseImage.kernelImagePath,
|
|
bootArgs: baseImage.bootArgs,
|
|
},
|
|
machineConfig: {
|
|
vcpuCount: 1,
|
|
memSizeMib: 256,
|
|
},
|
|
drives: [
|
|
{
|
|
driveId: 'rootfs',
|
|
pathOnHost: baseImage.rootfsPath,
|
|
isRootDevice: true,
|
|
isReadOnly: baseImage.rootfsIsReadOnly,
|
|
},
|
|
],
|
|
});
|
|
|
|
try {
|
|
await vm.start();
|
|
expect(vm.state).toEqual('running');
|
|
expect(await vm.getInfo()).toBeTruthy();
|
|
} finally {
|
|
if (vm.state === 'running' || vm.state === 'paused') {
|
|
await vm.stop();
|
|
}
|
|
await vm.cleanup();
|
|
await smartvm.cleanup();
|
|
}
|
|
});
|
|
|
|
export default tap.start();
|