feat(base-images): add managed base image bundles with cache retention, hosted manifests, and opt-in integration boot testing
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user