hacer imagen y kernel y correr qemu
This commit is contained in:
parent
e235497717
commit
51b29b7d3d
6 changed files with 162 additions and 9 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
build-javascript
|
build-javascript
|
||||||
cache/
|
cache/
|
||||||
|
artifacts/
|
|
@ -5,7 +5,6 @@ import {
|
||||||
mkdtemp,
|
mkdtemp,
|
||||||
opendir,
|
opendir,
|
||||||
rm,
|
rm,
|
||||||
rmdir,
|
|
||||||
symlink,
|
symlink,
|
||||||
writeFile,
|
writeFile,
|
||||||
} from "node:fs/promises";
|
} from "node:fs/promises";
|
||||||
|
|
23
helpers.ts
23
helpers.ts
|
@ -3,6 +3,29 @@ import {
|
||||||
execFile as execFileCallback,
|
execFile as execFileCallback,
|
||||||
spawn as spawnCallback,
|
spawn as spawnCallback,
|
||||||
} from "node:child_process";
|
} from "node:child_process";
|
||||||
|
import { access } from "node:fs/promises";
|
||||||
|
import { getuid } from "node:process";
|
||||||
|
|
||||||
export const execFile = promisify(execFileCallback);
|
export const execFile = promisify(execFileCallback);
|
||||||
export const spawn = promisify(spawnCallback);
|
export const spawn = promisify(spawnCallback);
|
||||||
|
|
||||||
|
export async function canAccess(path: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await access(path);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sudoChown(path: string, owner: string): Promise<void> {
|
||||||
|
await execFile("sudo", ["chown", owner, path]);
|
||||||
|
}
|
||||||
|
export async function sudoChownToRunningUser(path: string): Promise<void> {
|
||||||
|
if (getuid) {
|
||||||
|
await sudoChown(path, "" + getuid());
|
||||||
|
} else throw new Error("No tengo getuid");
|
||||||
|
}
|
||||||
|
export async function sudoRm(path: string): Promise<void> {
|
||||||
|
await execFile("sudo", ["rm", path]);
|
||||||
|
}
|
||||||
|
|
27
index.ts
27
index.ts
|
@ -3,23 +3,42 @@ import { tmpdir } from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { cwd } from "node:process";
|
import { cwd } from "node:process";
|
||||||
import { Alpine } from "./alpine.js";
|
import { Alpine } from "./alpine.js";
|
||||||
import { spawn } from "./helpers.js";
|
import { execFile, spawn, sudoChownToRunningUser } from "./helpers.js";
|
||||||
import { setupKernel } from "./kernel.js";
|
import { setupKernel } from "./kernel.js";
|
||||||
|
import { runQemu } from "./qemu.js";
|
||||||
import { Runit } from "./runit/index.js";
|
import { Runit } from "./runit/index.js";
|
||||||
|
|
||||||
{
|
{
|
||||||
console.time("Building");
|
console.time("Building");
|
||||||
|
|
||||||
|
const artifactsDir = path.join(cwd(), "artifacts/");
|
||||||
|
const kernelDir = path.join(artifactsDir, "kernel");
|
||||||
|
await mkdir(artifactsDir, { recursive: true });
|
||||||
|
await mkdir(kernelDir, { recursive: true });
|
||||||
|
|
||||||
const rootfsDir = await mkdtemp(path.join(tmpdir(), "define-alpine-"));
|
const rootfsDir = await mkdtemp(path.join(tmpdir(), "define-alpine-"));
|
||||||
console.debug(rootfsDir);
|
console.debug(rootfsDir);
|
||||||
const alpine = await Alpine.makeWorld({ dir: rootfsDir });
|
const alpine = await Alpine.makeWorld({ dir: rootfsDir });
|
||||||
const runit = await Runit.setup(alpine);
|
const runit = await Runit.setup(alpine);
|
||||||
await setupKernel(alpine);
|
const kernel = await setupKernel(alpine, kernelDir);
|
||||||
|
|
||||||
|
const squashfs = path.join(artifactsDir, "image.squashfs");
|
||||||
|
await execFile("sudo", [
|
||||||
|
"mksquashfs",
|
||||||
|
alpine.dir,
|
||||||
|
squashfs,
|
||||||
|
"-comp",
|
||||||
|
"zstd",
|
||||||
|
"-Xcompression-level",
|
||||||
|
"3",
|
||||||
|
"-noappend",
|
||||||
|
"-quiet",
|
||||||
|
]);
|
||||||
|
await sudoChownToRunningUser(squashfs);
|
||||||
|
|
||||||
console.timeEnd("Building");
|
console.timeEnd("Building");
|
||||||
|
|
||||||
const artifactsDir = path.join(cwd(), "artifacts/");
|
runQemu(squashfs, kernel, { graphic: false });
|
||||||
await mkdir(artifactsDir);
|
|
||||||
|
|
||||||
// await makeService({
|
// await makeService({
|
||||||
// parentDir: rootfsDir,
|
// parentDir: rootfsDir,
|
||||||
|
|
61
kernel.ts
61
kernel.ts
|
@ -1,6 +1,22 @@
|
||||||
import { Alpine } from "./alpine";
|
import { constants } from "node:fs";
|
||||||
|
import { copyFile } from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { Alpine } from "./alpine.js";
|
||||||
|
import { canAccess, sudoChownToRunningUser, sudoRm } from "./helpers.js";
|
||||||
|
|
||||||
|
export type Kind = "lts" | "virt";
|
||||||
|
export type Kernel = {
|
||||||
|
initramfs: string;
|
||||||
|
vmlinuz: string;
|
||||||
|
kind: Kind;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function setupKernel(
|
||||||
|
alpine: Alpine,
|
||||||
|
output: string
|
||||||
|
): Promise<Kernel> {
|
||||||
|
const kind: Kind = "virt";
|
||||||
|
|
||||||
export async function setupKernel(alpine: Alpine): Promise<void> {
|
|
||||||
await alpine.writeFile(
|
await alpine.writeFile(
|
||||||
"/etc/update-extlinux.conf",
|
"/etc/update-extlinux.conf",
|
||||||
`# configuration for extlinux config builder
|
`# configuration for extlinux config builder
|
||||||
|
@ -29,10 +45,47 @@ default=lts
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
"squashfs",
|
||||||
|
"ata",
|
||||||
|
"base",
|
||||||
|
"cdrom",
|
||||||
|
"ext4",
|
||||||
|
"keymap",
|
||||||
|
"kms",
|
||||||
|
"mmc",
|
||||||
|
"nvme",
|
||||||
|
"scsi",
|
||||||
|
"usb",
|
||||||
|
...(kind === "virt" ? ["virtio"] : []),
|
||||||
|
];
|
||||||
|
|
||||||
await alpine.writeFile(
|
await alpine.writeFile(
|
||||||
"/etc/mkinitfs/mkinitfs.conf",
|
"/etc/mkinitfs/mkinitfs.conf",
|
||||||
'features="squashfs ata base cdrom ext4 keymap kms mmc nvme scsi usb virtio"'
|
`features="${features.join(" ")}"`
|
||||||
);
|
);
|
||||||
|
|
||||||
await alpine.addPackages(["linux-virt"]);
|
await alpine.addPackages([`linux-${kind}`]);
|
||||||
|
|
||||||
|
const initramfs = path.join(alpine.dir, `/boot/initramfs-${kind}`);
|
||||||
|
const vmlinuz = path.join(alpine.dir, `/boot/vmlinuz-${kind}`);
|
||||||
|
|
||||||
|
if (!(await canAccess(initramfs)))
|
||||||
|
throw new Error("Didn't generate initramfs");
|
||||||
|
else if (!(await canAccess(vmlinuz)))
|
||||||
|
throw new Error("Didn't generate vmlinuz");
|
||||||
|
|
||||||
|
const kernel: Kernel = {
|
||||||
|
kind,
|
||||||
|
initramfs: path.join(output, "initramfs"),
|
||||||
|
vmlinuz: path.join(output, "vmlinuz"),
|
||||||
|
};
|
||||||
|
|
||||||
|
await sudoChownToRunningUser(initramfs);
|
||||||
|
await sudoChownToRunningUser(vmlinuz);
|
||||||
|
await copyFile(initramfs, kernel.initramfs, constants.COPYFILE_FICLONE);
|
||||||
|
await copyFile(vmlinuz, kernel.vmlinuz, constants.COPYFILE_FICLONE);
|
||||||
|
await sudoRm(initramfs);
|
||||||
|
await sudoRm(vmlinuz);
|
||||||
|
return kernel;
|
||||||
}
|
}
|
||||||
|
|
58
qemu.ts
Normal file
58
qemu.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { mkdtemp, rm } from "node:fs/promises";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { execFile } from "./helpers.js";
|
||||||
|
import { Kernel } from "./kernel.js";
|
||||||
|
|
||||||
|
export async function runQemu(
|
||||||
|
squashfs: string,
|
||||||
|
kernel: Kernel,
|
||||||
|
{ graphic }: { graphic: boolean } = { graphic: true }
|
||||||
|
) {
|
||||||
|
const tmp = await mkdtemp(path.join(tmpdir(), "define-alpine-qemu-"));
|
||||||
|
try {
|
||||||
|
const disk = path.join(tmp, "tmp.ext4");
|
||||||
|
await execFile("fallocate", ["--length", "1G", disk]);
|
||||||
|
await execFile("mkfs.ext4", ["-F", disk]);
|
||||||
|
|
||||||
|
let kernelAppend = [
|
||||||
|
"root=/dev/sda",
|
||||||
|
"rootfstype=squashfs",
|
||||||
|
"modules=ext4",
|
||||||
|
"quiet",
|
||||||
|
"init=/sbin/runit-init",
|
||||||
|
];
|
||||||
|
let qemuAppend: string[] = [];
|
||||||
|
|
||||||
|
if (!graphic) {
|
||||||
|
kernelAppend.push("console=ttyS0");
|
||||||
|
qemuAppend.push("-nographic");
|
||||||
|
}
|
||||||
|
|
||||||
|
// sudo chown root:$(id -u) -R boot/ && sudo chmod g+rw -R boot/
|
||||||
|
await execFile("qemu-system-x86_64", [
|
||||||
|
"-enable-kvm",
|
||||||
|
"-m",
|
||||||
|
"2048",
|
||||||
|
"-drive",
|
||||||
|
`file=${squashfs},media=disk,format=raw`,
|
||||||
|
"-drive",
|
||||||
|
`file=${disk},media=disk,format=raw`,
|
||||||
|
"-net",
|
||||||
|
"nic,model=virtio-net-pci",
|
||||||
|
"-net",
|
||||||
|
"user,hostfwd=tcp:127.0.0.1:8080-:80",
|
||||||
|
"-kernel",
|
||||||
|
kernel.vmlinuz,
|
||||||
|
"-initrd",
|
||||||
|
kernel.initramfs,
|
||||||
|
"-append",
|
||||||
|
kernelAppend.join(" "),
|
||||||
|
...qemuAppend,
|
||||||
|
"-no-shutdown",
|
||||||
|
]);
|
||||||
|
// -append "root=/dev/sda rootfstype=squashfs modules=ext4 quiet init=/sbin/runit-init $append" $qemuappend
|
||||||
|
} finally {
|
||||||
|
await rm(tmp, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue