diff --git a/scripts/resource.json b/scripts/resource.json index f5e6132..0e74959 100644 --- a/scripts/resource.json +++ b/scripts/resource.json @@ -2,39 +2,32 @@ "darwin-x64": [ { "name": "ovm", - "version": "v2.0.0-RC8", - "download": "https://static.oomol.com/ovm-resources/ovm/{version}/ovm-amd64", - "sha256": "7f576fe11b62a8af8e932401c4749d26b3745d46cefd65bd3091ebf010ba837c", - "out": "ovm" - }, - { - "name": "rootfs", - "version": "v1.0.3", - "download": "https://static.oomol.com/ovm-resources/ovm-core/{version}/applehv-rootfs-amd64.rootfs.erofs", - "sha256": "7436bb01151154bd967af286435e65194da66570760cdc50e26a3702ac7ff87a", - "out": "rootfs.erofs" + "version": "v1.0.0-RC7", + "download": "https://static.oomol.com/ovm-resources/Bauklotze/{version}/ovm-amd64", + "sha256": "40bb26e0859b1246c7e862568ec3d46c9975931559288f9449cbf091431fe637", + "out": "bin/ovm" }, { - "name": "initrd", - "version": "v1.0.3", - "download": "https://static.oomol.com/ovm-resources/ovm-core/{version}/initrd-amd64.initrd.gz", - "sha256": "064eaf482a0ed8b8b7071d4b85f37ff26e201ef844566cac6b6406b8724cb698", - "out": "initrd.gz" + "name": "bootable", + "version": "v0.0.1", + "download": "https://static.oomol.com/ovm-resources/Bauklotze/{version}/bootable-darwin-amd64.img.zst", + "sha256": "8d1da14847745cd6f76927eed39d8fa4fa81027149261e58a3c9aca0d1d985f7", + "out": "bootable.img.zst" }, { - "name": "kernel", - "version": "v1.0.3", - "download": "https://static.oomol.com/ovm-resources/ovm-core/{version}/kernel-amd64.bzImage", - "sha256": "fba25a5e0019f25cb43cb18571eb1e4d816324716de1cd98d14defca397ca158", - "out": "bzImage" + "name": "libexec", + "version": "v0.0.1", + "download": "https://static.oomol.com/ovm-resources/Bauklotze/{version}/libexec-darwin-amd64.tar.gz", + "sha256": "632de5605647b19d2c50211592cb2b9b9b4ab918f828540425069657526e7baf", + "out": "libexec.tar.gz" } ], "darwin-arm64": [ { "name": "ovm", - "version": "v1.0.0-RC6", + "version": "v1.0.0-RC7", "download": "https://static.oomol.com/ovm-resources/Bauklotze/{version}/ovm-arm64", - "sha256": "8b658e81dc7ec73a6f45dd33a397db01758ea70d7f8687fd600aaba536a39137", + "sha256": "362a8983e805fb9efd2caaea4b95be06b4a0e3b547d7c52044950f1c8b1c406e", "out": "bin/ovm" }, { diff --git a/src/darwin_arm64.ts b/src/darwin.ts similarity index 80% rename from src/darwin_arm64.ts rename to src/darwin.ts index 9c741b5..27dc631 100644 --- a/src/darwin_arm64.ts +++ b/src/darwin.ts @@ -1,29 +1,29 @@ -import { RequestDarwinArm64 } from "./request"; +import { RequestDarwin } from "./request"; import cp from "node:child_process"; import type { EventReceiver } from "remitter"; import { Remitter } from "remitter"; -import type { OVMDarwinArm64InitEvent, OVMDarwinArm64Options, OVMDarwinArm64StartEvent } from "./type"; +import type { OVMDarwinInitEvent, OVMDarwinOptions, OVMDarwinStartEvent } from "./type"; import { Restful } from "./event_restful"; import fs from "node:fs/promises"; import path from "node:path"; import { tmpdir } from "node:os"; -import { enableDebug, resourceArm64 } from "./utils"; +import { enableDebug, resource } from "./utils"; -export class DarwinOVMArm64 extends RequestDarwinArm64 { +export class DarwinOVM extends RequestDarwin { public readonly events: { - init: EventReceiver; - start: EventReceiver; + init: EventReceiver; + start: EventReceiver; }; readonly #events: { - init: Remitter; - start: Remitter; + init: Remitter; + start: Remitter; }; private restful: Record<"init" | "start", Restful>; private restfulWithInit: string; private restfulWithStart: string; - private constructor(private options: OVMDarwinArm64Options) { + private constructor(private options: OVMDarwinOptions) { super(options.workspace); this.events = this.#events = { init: new Remitter(), @@ -31,11 +31,11 @@ export class DarwinOVMArm64 extends RequestDarwinArm64 { }; } - public static async create(options: OVMDarwinArm64Options): Promise { + public static async create(options: OVMDarwinOptions): Promise { await fs.mkdir(options.workspace, { recursive: true, }); - const ovm = new DarwinOVMArm64(options); + const ovm = new DarwinOVM(options); await Promise.all([ ovm.initEventRestful(), ]); @@ -50,13 +50,13 @@ export class DarwinOVMArm64 extends RequestDarwinArm64 { this.#events.init.remitAny((o) => { return this.restful.init.events.onAny((data) => { - o.emit(data.event as keyof OVMDarwinArm64InitEvent, data.data); + o.emit(data.event as keyof OVMDarwinInitEvent, data.data); }); }); this.#events.start.remitAny((o) => { return this.restful.start.events.onAny((data) => { - o.emit(data.event as keyof OVMDarwinArm64StartEvent, data.data); + o.emit(data.event as keyof OVMDarwinStartEvent, data.data); }); }); @@ -82,13 +82,13 @@ export class DarwinOVMArm64 extends RequestDarwinArm64 { }); }); - const ovmBin = resourceArm64("ovm", this.options.resource); + const ovmBin = resource("ovm", this.options.resource); const ovmArgs = [ "machine", "init", "--cpus", String(this.options.cpu), "--memory", String(this.options.memory), - "--boot", resourceArm64("image", this.options.resource), + "--boot", resource("image", this.options.resource), "--boot-version", this.options.versions.image, "--data-version", this.options.versions.data, "--report-url", `unix://${this.restfulWithInit}`, @@ -137,7 +137,7 @@ export class DarwinOVMArm64 extends RequestDarwinArm64 { }); }); - const ovmBin = resourceArm64("ovm", this.options.resource); + const ovmBin = resource("ovm", this.options.resource); const ovmArgs = [ "machine", "start", diff --git a/src/darwin_x64.ts b/src/darwin_x64.ts deleted file mode 100644 index 6784e9b..0000000 --- a/src/darwin_x64.ts +++ /dev/null @@ -1,116 +0,0 @@ -import cp from "node:child_process"; -import fs from "node:fs/promises"; -import type { EventReceiver } from "remitter"; -import { Remitter } from "remitter"; -import type { OVMDarwinEventData, OVMDarwinOptions } from "./type"; -import { Restful } from "./event_restful"; -import { RequestDarwin } from "./request"; -import path from "node:path"; -import { tmpdir } from "node:os"; -import { enableDebug, resource } from "./utils"; - -export class DarwinOVM extends RequestDarwin { - public readonly events : EventReceiver; - readonly #events: Remitter; - private restful: Restful; - private eventSocketPath: string; - - private constructor(private options: OVMDarwinOptions) { - super(options.socketDir, options.name); - this.events = this.#events = new Remitter(); - } - - public static async create(options: OVMDarwinOptions): Promise { - const ovm = new DarwinOVM(options); - await Promise.all([ - ovm.initEventRestful(), - ovm.initPath(), - ]); - return ovm; - } - - private async initEventRestful(): Promise { - this.restful = new Restful(); - - this.#events.remitAny((o) => { - return this.restful.events.onAny((data) => { - o.emit(data.event as keyof OVMDarwinEventData, data.data); - }); - }); - - const dir = await fs.mkdtemp(path.join(tmpdir(), "ovm-")); - const socketPath = path.join(dir, "event-restful.sock"); - this.restful.start(socketPath); - this.eventSocketPath = socketPath; - } - - private async initPath(): Promise { - await fs.mkdir(this.options.targetDir, { - recursive: true, - mode: 0o755, - }); - } - - public start(): void { - const versions = Object.keys(this.options.versions).map((key) => { - return `${key}=${this.options.versions[key]}`; - }).join(","); - - const launchTimeout = new Promise((resolve, reject) => { - const id = setTimeout(() => { - disposer(); - // eslint-disable-next-line prefer-promise-reject-errors - reject(); - }, 10 * 1000); - - const disposer = this.#events.onceAny(() => { - clearTimeout(id); - resolve(); - }); - }); - - const ovmBin = resource("ovm", this.options.resource); - const ovmArgs = [ - "-name", this.options.name, - "-log-path", this.options.logDir, - "-socket-path", this.options.socketDir, - "-ssh-key-path", this.options.sshKeyDir, - "-cpus", String(this.options.cpu), - "-memory", String(this.options.memory), - "-kernel-path", resource("kernel", this.options.resource), - "-initrd-path", resource("initrd", this.options.resource), - "-rootfs-path", resource("rootfs", this.options.resource), - "-target-path", this.options.targetDir, - "-versions", versions, - "-event-socket-path", this.eventSocketPath, - "-bind-pid", String(this.options.bindPID || process.pid), - `-power-save-mode=${String(this.options.powerSaveMode)}`, - `-extend-share-dir=${this.options.extendShareDir || ""}`, - ]; - - if (enableDebug()) { - console.log(`[OVM] executing: ${ovmBin} ${ovmArgs.join(" ")}`); - console.log(`[OVM] cwd: ${this.options.cwd}`); - } - - const ovm = cp.spawn(ovmBin, ovmArgs, { - timeout: 0, - windowsHide: true, - detached: true, - stdio: "ignore", - cwd: this.options.cwd, - }); - - ovm.unref(); - - launchTimeout - .catch(() => { - this.#events.emit("error", "OVM start timeout"); - }); - } - - public dispose(): void { - this.restful.stop(); - this.#events.dispose(); - } -} diff --git a/src/index.ts b/src/index.ts index 7f8be55..253739e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,34 +1,25 @@ -import type { OVMDarwinArm64Options, OVMDarwinOptions, OVMWindowsOptions } from "./type"; -import { DarwinOVM } from "./darwin_x64"; +import type { OVMDarwinOptions, OVMWindowsOptions } from "./type"; +import { DarwinOVM } from "./darwin"; import { WindowsOVM } from "./windows"; -import { DarwinOVMArm64 } from "./darwin_arm64"; export const createDarwinOVM = (options: OVMDarwinOptions): Promise => { return DarwinOVM.create(options); }; -export const createDarwinArm64OVM = (options: OVMDarwinArm64Options): Promise => { - return DarwinOVMArm64.create(options); -}; - export const createWindowsOVM = (options: OVMWindowsOptions): WindowsOVM => { return WindowsOVM.create(options); }; -export { OVMDarwinAppEventValue, OVMDarwinVzState, OVMWindowsRunEventValue, OVMWindowsPrepareEventValue } from "./type"; +export { OVMWindowsRunEventValue, OVMWindowsPrepareEventValue } from "./type"; export type { - OVMDarwinEventData, OVMDarwinOptions, + OVMDarwinInitEvent, + OVMDarwinStartEvent, OVMDarwinInfo, OVMDarwinState, - OVMDarwinArm64Options, - OVMDarwinArm64InitEvent, - OVMDarwinArm64StartEvent, - OVMDarwinArm64Info, - OVMDarwinArm64State, OVMWindowsOptions, OVMWindowsEventData, OVMWindowsInfo, } from "./type"; -export type { DarwinOVM, DarwinOVMArm64, WindowsOVM }; +export type { DarwinOVM, WindowsOVM }; diff --git a/src/request.ts b/src/request.ts index 58d1a94..912c728 100644 --- a/src/request.ts +++ b/src/request.ts @@ -1,13 +1,7 @@ import path from "node:path"; import http from "node:http"; import { Readable } from "node:stream"; -import type { - OVMDarwinArm64Info, - OVMDarwinArm64State, - OVMDarwinInfo, - OVMDarwinState, - OVMWindowsInfo, -} from "./type"; +import type { OVMDarwinInfo, OVMDarwinState, OVMWindowsInfo } from "./type"; import { createEventSource } from "eventsource-client"; import fetch from "node-fetch"; import { enableDebug } from "./utils"; @@ -22,7 +16,7 @@ const DEFAULT_TIMEOUT = 200; const NEVER_TIMEOUT = 0; abstract class Request { - public abstract info(): Promise; + public abstract info(): Promise; protected readonly socketPath: string; @@ -122,35 +116,7 @@ abstract class Request { } } -export class RequestDarwin extends Request { - public constructor(socketDir: string, name: string) { - super(path.join(socketDir, `${name}-restful.sock`)); - } - - public async info(): Promise { - return JSON.parse(await this.do("info", Method.GET)) as Promise; - } - - public async state(): Promise { - return JSON.parse(await this.do("state", Method.GET)) as Promise; - } - - public async pause(): Promise { - await this.do("pause", Method.POST); - } - - public async resume(): Promise { - await this.do("resume", Method.POST); - } - - public async powerSaveMode(enable: boolean): Promise { - await this.do("power-save-mode", Method.PUT, DEFAULT_TIMEOUT, { - enable, - }); - } -} - -type RequestDarwinArm64RawInfoResp = { +type RequestDarwinRawInfoResp = { GvProxy: { HostSocks: [string]; }; @@ -161,13 +127,13 @@ type RequestDarwinArm64RawInfoResp = { } } -export class RequestDarwinArm64 extends Request { +export class RequestDarwin extends Request { public constructor(workspace: string) { super(path.join(workspace, "tmp", "ovm_restapi.socks"), "http://ovm/default/"); } - public async info(): Promise { - const result = JSON.parse(await this.do("info", Method.GET)) as RequestDarwinArm64RawInfoResp; + public async info(): Promise { + const result = JSON.parse(await this.do("info", Method.GET)) as RequestDarwinRawInfoResp; return { podmanSocketPath: result.GvProxy.HostSocks[0], sshPort: result.SSH.Port, @@ -177,8 +143,8 @@ export class RequestDarwinArm64 extends Request { }; } - public async state(): Promise { - const result = JSON.parse(await this.do("vmstat", Method.GET)) as { CurrentStat: OVMDarwinArm64State["state"] }; + public async state(): Promise { + const result = JSON.parse(await this.do("vmstat", Method.GET)) as { CurrentStat: OVMDarwinState["state"] }; return { state: result.CurrentStat, canStop: true, diff --git a/src/type.ts b/src/type.ts index 5a77666..6767100 100644 --- a/src/type.ts +++ b/src/type.ts @@ -1,80 +1,5 @@ +// ----- darwin export interface OVMDarwinOptions { - name: string; - cpu: number; - memory: number; - resource?: { - ovm?: string; - initrd?: string; - kernel?: string; - rootfs?: string; - } | string; - targetDir: string; - socketDir: string; - logDir: string; - sshKeyDir: string; - versions:{ - initrd: string; - kernel: string; - rootfs: string; - data: string; - }; - bindPID?: number | string; - powerSaveMode: boolean; - extendShareDir?: string, - cwd: string; -} - -export enum OVMDarwinAppEventValue { - Initializing = "Initializing", - GVProxyReady = "GVProxyReady", - IgnitionProgress = "IgnitionProgress", - IgnitionDone = "IgnitionDone", - Ready = "Ready", -} - -export interface OVMDarwinEventData { - app: OVMDarwinAppEventValue, - error: string, - exit: void, -} - -export interface OVMDarwinInfo { - podmanSocketPath: string, - sshPort: number, - sshUser: string, - sshPublicKeyPath: string, - sshPrivateKeyPath: string, - sshPublicKey: string, - sshPrivateKey: string, -} - -/** - * @see https://github.com/Code-Hex/vz/blob/bd29a7ea3d39465c4224bfb01e990e8c220a8449/virtualization.go#L23 - */ -export enum OVMDarwinVzState { - VirtualMachineStateStopped = "VirtualMachineStateStopped", - VirtualMachineStateRunning = "VirtualMachineStateRunning", - VirtualMachineStatePaused = "VirtualMachineStatePaused", - VirtualMachineStateError = "VirtualMachineStateError", - VirtualMachineStateStarting = "VirtualMachineStateStarting", - VirtualMachineStatePausing = "VirtualMachineStatePausing", - VirtualMachineStateResuming = "VirtualMachineStateResuming", - VirtualMachineStateStopping = "VirtualMachineStateStopping", - VirtualMachineStateSaving = "VirtualMachineStateSaving", - VirtualMachineStateRestoring = "VirtualMachineStateRestoring", -} - -export interface OVMDarwinState { - state: OVMDarwinVzState; - canStart: boolean; - canPause: boolean; - canResume: boolean; - canRequestStop: boolean; - canStop: boolean; -} - -// ----- darwin arm64 -export interface OVMDarwinArm64Options { cpu: number; memory: number; resource?: { @@ -90,21 +15,21 @@ export interface OVMDarwinArm64Options { appendVolume?: string[]; } -export interface OVMDarwinArm64InitEvent { +export interface OVMDarwinInitEvent { decompress: "running" | "success"; writeConfig: "running" | "success"; exit: void; error: string; } -export interface OVMDarwinArm64StartEvent { +export interface OVMDarwinStartEvent { start: string; ready: void; exit: void; error: string; } -export interface OVMDarwinArm64Info { +export interface OVMDarwinInfo { podmanSocketPath: string, sshPort: number, sshUser: string, @@ -112,7 +37,7 @@ export interface OVMDarwinArm64Info { sshPrivateKeyPath: string, } -export interface OVMDarwinArm64State { +export interface OVMDarwinState { state: "Running" | "Stopped"; canStart: boolean; canPause: boolean; diff --git a/src/utils.ts b/src/utils.ts index 58d008a..7abfc1c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,12 +1,12 @@ import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; -import type { OVMDarwinArm64Options, OVMDarwinOptions, OVMWindowsOptions } from "./type"; +import type { OVMDarwinOptions, OVMWindowsOptions } from "./type"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const resourcesPath = join(__dirname, "..", "vm-resources"); -export const resource = (name: "rootfs" | "initrd" | "kernel" | "ovm", resource: OVMDarwinOptions["resource"] | OVMWindowsOptions["resource"]): string => { +export const resource = (name: "rootfs" | "ovm" | "image", resource: OVMDarwinOptions["resource"] | OVMWindowsOptions["resource"]): string => { if (typeof resource !== "string" && resource?.[name] !== undefined) { return resource[name]; } @@ -15,36 +15,27 @@ export const resource = (name: "rootfs" | "initrd" | "kernel" | "ovm", resource: switch (name) { case "rootfs": { - const file = process.platform === "darwin" ? "rootfs.erofs" : "rootfs.zst"; - return join(path, file); - } - case "initrd": { - return join(path, "initrd.gz"); - } - case "kernel": { - const file = process.arch === "x64" ? "bzImage" : "Image"; - return join(path, file); - } - case "ovm": { - const file = process.platform === "darwin" ? "ovm" : "ovm.exe"; - return join(path, file); - } - } -}; + if (process.platform === "win32") { + return join(path, "rootfs.zst"); + } -export const resourceArm64 = (name: "ovm" | "image", resource: OVMDarwinArm64Options["resource"]): string => { - if (typeof resource !== "string" && resource?.[name] !== undefined) { - return resource[name]; - } - - const path = typeof resource === "string" ? resource : resourcesPath; + throw new Error("Not supported rootfs in Darwin"); + } - switch (name) { case "ovm": { + if (process.platform === "win32") { + return join(path, "ovm.exe"); + } + return join(path, "bin", "ovm"); } + case "image": { - return join(path, "bootable.img.zst"); + if (process.platform === "darwin") { + return join(path, "bootable.img.zst"); + } + + throw new Error("Not supported image in Windows"); } } };