Skip to content

Commit 2b96b34

Browse files
committed
feat: added wsl backend
1 parent b74f7ee commit 2b96b34

File tree

6 files changed

+138
-141
lines changed

6 files changed

+138
-141
lines changed

src/index.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1+
import { ChildProcess, exec } from "child_process";
2+
13
import BaseBackend from "@/vms/base";
24
import LimaBackend from "@/vms/lima";
5+
import WslBackend from "@/vms/wsl";
36
import os from "os";
47
import { platform } from "./utils";
58

6-
export function factory(): BaseBackend {
9+
export function factory(
10+
vm?: string,
11+
instance: string = "default"
12+
): BaseBackend {
713
const arch = os.arch() === "arm64" ? "aarch64" : "x86_64";
814
switch (platform) {
915
case "linux":
10-
return new LimaBackend(arch);
16+
return new LimaBackend(arch, vm, instance);
1117
case "darwin":
12-
return new LimaBackend(arch);
18+
return new LimaBackend(arch, vm, instance);
19+
case "win32":
20+
return new WslBackend(arch, vm, instance);
1321
default:
1422
throw new Error(`OS "${platform}" is not supported.`);
1523
}

src/tests/images.test.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1+
import { ChildProcess } from "child_process";
12
import { factory } from "..";
23

34
describe("images", () => {
4-
const engine = factory();
5+
const engine = factory("wsl -u vmenv");
56
const IMAGE = "hello-world";
67

78
test("pull image", async () => {
8-
const result = await engine.pullImage(IMAGE);
9-
expect(result.exitCode).toBeNull();
9+
const child = (await engine.pullImage(IMAGE)) as ChildProcess;
10+
const result = await new Promise((resolve, reject) => {
11+
child?.stdout?.on("data", (data) => {
12+
console.log(data);
13+
});
14+
child?.stdout?.on("close", () => {
15+
resolve(true);
16+
});
17+
child?.stderr?.on("data", (data) => {
18+
console.log(data);
19+
});
20+
child?.stderr?.on("close", () => {
21+
reject(false);
22+
});
23+
});
24+
expect(result).toEqual(true);
1025
});
1126
});

src/tests/vm.test.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/vms/base.ts

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
LogsCommandFlags,
55
RmCommandFlags,
66
RunCommandFlags,
7+
StopCommandFlags,
78
} from "@/types/container";
89

910
import { ChildProcess } from "child_process";
@@ -22,12 +23,19 @@ export default abstract class BaseBackend {
2223
protected readonly instance: string = "default";
2324
protected readonly runtime: string = "nerdctl";
2425

25-
constructor(arch: Architecture, instance: string = "default") {
26+
constructor(
27+
arch: Architecture,
28+
vm: string = "limactl",
29+
instance: string = "default"
30+
) {
2631
this.arch = arch;
32+
this.vm = vm;
2733
this.instance = instance;
2834
}
2935

3036
get container() {
37+
if (this.platform !== "darwin")
38+
return `${this.vm} -e XDG_RUNTIME_DIR=/run/user/$(id -u) ${this.runtime}`;
3139
return `${this.vm} shell ${this.instance} ${this.runtime}`;
3240
}
3341

@@ -67,31 +75,89 @@ export default abstract class BaseBackend {
6775
}
6876

6977
abstract initVM(): Promise<boolean>;
70-
abstract startVM(): Promise<ChildProcess>;
78+
abstract startVM(): Promise<ChildProcess | null>;
7179
abstract stopVM(): Promise<void>;
7280
abstract deleteVM(): Promise<void>;
7381

74-
abstract login(
82+
//#region registry
83+
async login(
7584
flags?: LoginCommandFlags,
7685
server?: string
77-
): Promise<ShellString>;
86+
): Promise<ShellString> {
87+
const command = `${this.container} login ${this.mergeFlags(
88+
flags
89+
)} ${server}`;
7890

79-
abstract run(image: string, flags?: RunCommandFlags): Promise<ChildProcess>;
91+
return (await this.exec(command, { async: false })) as ShellString;
92+
}
93+
//#endregion
94+
95+
//#region containers
96+
async run(image: string, flags?: RunCommandFlags): Promise<ChildProcess> {
97+
const command = `${this.container} run ${this.mergeFlags(flags)} ${image}`;
98+
return (await this.exec(command)) as ChildProcess;
99+
}
80100

81-
abstract stop(
101+
async stop(
82102
container: string | string[],
83-
flags?: RmCommandFlags
84-
): Promise<ShellString>;
85-
abstract rm(
103+
flags?: StopCommandFlags
104+
): Promise<ShellString> {
105+
const containers = Array.isArray(container)
106+
? container.join(" ")
107+
: container;
108+
return (await this.exec(
109+
`${this.container} stop ${this.mergeFlags(flags)} ${containers}`,
110+
{ async: false }
111+
)) as ShellString;
112+
}
113+
114+
async rm(
86115
container: string | string[],
87116
flags?: RmCommandFlags
88-
): Promise<ShellString>;
117+
): Promise<ShellString> {
118+
const containers = Array.isArray(container)
119+
? container.join(" ")
120+
: container;
121+
return (await this.exec(
122+
`${this.container} rm ${this.mergeFlags(flags)} ${containers}`,
123+
{ async: false }
124+
)) as ShellString;
125+
}
89126

90-
abstract logs(
127+
async logs(
91128
container: string,
92129
flags?: LogsCommandFlags
93-
): Promise<ChildProcess>;
130+
): Promise<ChildProcess> {
131+
return (await this.exec(
132+
`${this.container} logs ${this.mergeFlags(flags)} ${container}`
133+
)) as ChildProcess;
134+
}
135+
//#endregion
94136

95-
abstract pullImage(image: string): Promise<ChildProcess>;
96-
abstract getImages(): Promise<ImageResult[]>;
137+
//#region images
138+
async pullImage(image: string): Promise<ChildProcess> {
139+
return (await this.exec(`${this.container} pull ${image}`)) as ChildProcess;
140+
}
141+
142+
async getImages(): Promise<ImageResult[]> {
143+
const child = (await this.exec(
144+
`${this.container} images --format "{{json .}}"`
145+
)) as ChildProcess;
146+
147+
return new Promise((resolve, reject) => {
148+
if (!!child.exitCode) reject(null);
149+
150+
const images: ImageResult[] = [];
151+
152+
child.stdout!.on("data", (data) => {
153+
if (!data) return;
154+
images.push(JSON.parse(data));
155+
});
156+
157+
child.stdout!.on("close", () => {
158+
resolve(images);
159+
});
160+
});
161+
}
162+
//#endregion
97163
}

src/vms/lima.ts

Lines changed: 1 addition & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
1-
import {
2-
LogsCommandFlags,
3-
RmCommandFlags,
4-
RunCommandFlags,
5-
StopCommandFlags,
6-
} from "@/types/container";
7-
import { ShellString, which } from "shelljs";
8-
91
import BaseBackend from "./base";
102
import { ChildProcess } from "child_process";
11-
import { ImageResult } from "@/types/images";
123
import { LimaListResult } from "@/types/lima";
13-
import { LoginCommandFlags } from "@/types/registry";
4+
import { which } from "shelljs";
145

156
export default class LimaBackend extends BaseBackend {
167
async initVM(): Promise<boolean> {
@@ -73,86 +64,4 @@ export default class LimaBackend extends BaseBackend {
7364
async deleteVM(): Promise<void> {
7465
await this.exec(`${this.vm} delete ${this.instance}`);
7566
}
76-
77-
//#region registry
78-
async login(
79-
flags?: LoginCommandFlags,
80-
server?: string
81-
): Promise<ShellString> {
82-
const command = `${this.container} login ${this.mergeFlags(
83-
flags
84-
)} ${server}`;
85-
86-
return (await this.exec(command, { async: false })) as ShellString;
87-
}
88-
//#endregion
89-
90-
//#region containers
91-
async run(image: string, flags?: RunCommandFlags): Promise<ChildProcess> {
92-
const command = `${this.container} run ${this.mergeFlags(flags)} ${image}`;
93-
return (await this.exec(command)) as ChildProcess;
94-
}
95-
96-
async stop(
97-
container: string | string[],
98-
flags?: StopCommandFlags
99-
): Promise<ShellString> {
100-
const containers = Array.isArray(container)
101-
? container.join(" ")
102-
: container;
103-
return (await this.exec(
104-
`${this.container} stop ${this.mergeFlags(flags)} ${containers}`,
105-
{ async: false }
106-
)) as ShellString;
107-
}
108-
109-
async rm(
110-
container: string | string[],
111-
flags?: RmCommandFlags
112-
): Promise<ShellString> {
113-
const containers = Array.isArray(container)
114-
? container.join(" ")
115-
: container;
116-
return (await this.exec(
117-
`${this.container} rm ${this.mergeFlags(flags)} ${containers}`,
118-
{ async: false }
119-
)) as ShellString;
120-
}
121-
122-
async logs(
123-
container: string,
124-
flags?: LogsCommandFlags
125-
): Promise<ChildProcess> {
126-
return (await this.exec(
127-
`${this.container} logs ${this.mergeFlags(flags)} ${container}`
128-
)) as ChildProcess;
129-
}
130-
//#endregion
131-
132-
//#region images
133-
async pullImage(image: string): Promise<ChildProcess> {
134-
return (await this.exec(`${this.container} pull ${image}`)) as ChildProcess;
135-
}
136-
137-
async getImages(): Promise<ImageResult[]> {
138-
const child = (await this.exec(
139-
`${this.container} images --format "{{json .}}"`
140-
)) as ChildProcess;
141-
142-
return new Promise((resolve, reject) => {
143-
if (!!child.exitCode) reject(null);
144-
145-
const images: ImageResult[] = [];
146-
147-
child.stdout!.on("data", (data) => {
148-
if (!data) return;
149-
images.push(JSON.parse(data));
150-
});
151-
152-
child.stdout!.on("close", () => {
153-
resolve(images);
154-
});
155-
});
156-
}
157-
//#endregion
15867
}

src/vms/wsl.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Architecture } from "@/types";
2+
import BaseBackend from "./base";
3+
import { ChildProcess } from "child_process";
4+
import { which } from "shelljs";
5+
6+
export default class WslBackend extends BaseBackend {
7+
constructor(
8+
protected readonly arch: Architecture,
9+
protected readonly vm: string = "wsl",
10+
protected readonly instance: string = "default"
11+
) {
12+
super(arch, vm, instance);
13+
}
14+
15+
async initVM(): Promise<boolean> {
16+
if (!which("wsl")) return false;
17+
if (!which(this.runtime)) return false;
18+
return true;
19+
}
20+
21+
async startVM(): Promise<ChildProcess | null> {
22+
return null;
23+
}
24+
25+
async stopVM(): Promise<void> {}
26+
27+
async deleteVM(): Promise<void> {}
28+
}

0 commit comments

Comments
 (0)