Inject universal /slim/init instead of requiring container-specific setup
CI / build (pull_request) Successful in 12s
CI / build (pull_request) Successful in 12s
slim now works with any Containerfile by injecting distro-agnostic scripts at build time: - /slim/init: mounts special filesystems, configures networking (static QEMU slirp), sets up cgroup2, timezone, and rootless container prereqs. Parses slim.cmd=<base64> from the kernel cmdline for runtime overrides, otherwise execs /slim/exec. - /slim/exec: generated from the image's CMD/ENTRYPOINT (via podman image inspect), overridable via --cmd at build and run time. Changes: - New src/inject.rs: inspect image config, infer command, generate /slim/exec script, inject /slim/ into mounted rootfs - New src/scripts/slim-init.sh: the universal init script (include_str!) - build.rs: rename --init to --cmd, inject scripts before packing, drop Meta::save, restructure to ensure unmount always runs - qemu.rs: hardcode init=/slim/init, add --cmd (base64 on cmdline), drop Meta::load, add -no-reboot - Remove src/meta.rs and meta.toml (no longer needed) - Cargo.toml: add serde_json + base64, remove unused walkdir + cpio + toml - Example Containerfiles simplified to plain FROM + CMD - New example/test.sh for manual verification - README updated for new workflow
This commit is contained in:
+36
-25
@@ -1,13 +1,17 @@
|
||||
//! QEMU: boot a registry entry with qemu-system-x86_64 using direct kernel
|
||||
//! boot (-kernel/-initrd).
|
||||
//! boot (-kernel/-initrd or -drive). The kernel is shared across all VMs
|
||||
//! at $XDG_DATA_HOME/slim-rs/vmlinuz. A universal /slim/init script (injected
|
||||
//! at build time) handles VM bootstrap; /slim/exec runs the container's
|
||||
//! CMD/ENTRYPOINT. Runtime overrides are delivered as `slim.cmd=<base64>`
|
||||
//! on the kernel cmdline.
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use base64::Engine;
|
||||
use clap::Args;
|
||||
use std::{fmt::Write as _, process::Command};
|
||||
|
||||
use crate::{
|
||||
forward::PortForward,
|
||||
meta::Meta,
|
||||
registry::{registry_base_dir, registry_dir},
|
||||
};
|
||||
|
||||
@@ -23,6 +27,11 @@ pub struct RunCmd {
|
||||
/// Forward ports from host to guest. Example: `tcp:0.0.0.0:80-:8080`
|
||||
#[clap(long)]
|
||||
forward: Vec<PortForward>,
|
||||
|
||||
/// Override the command to exec in the VM (base64-encoded on the kernel
|
||||
/// cmdline as slim.cmd=<b64>). Overrides the CMD inferred at build time.
|
||||
#[clap(long)]
|
||||
cmd: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn run(
|
||||
@@ -30,6 +39,7 @@ pub(crate) fn run(
|
||||
name,
|
||||
memory,
|
||||
forward,
|
||||
cmd,
|
||||
}: RunCmd,
|
||||
) -> Result<()> {
|
||||
let reg_dir = registry_dir(&name)?;
|
||||
@@ -38,40 +48,40 @@ pub(crate) fn run(
|
||||
let qcow2_path = reg_dir.join("image.qcow2");
|
||||
if !vmlinuz_path.exists() {
|
||||
// TODO: guide user in how to set up a kernel
|
||||
bail!("No kernel available.")
|
||||
bail!(
|
||||
"No kernel available. Place a vmlinuz at {}",
|
||||
vmlinuz_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
let meta = Meta::load(&name).context("Failed to load meta.toml. Run `slim build` first.")?;
|
||||
// TODO: sanity-check for spaces
|
||||
let init_arg = format!("init={}", meta.init);
|
||||
|
||||
let fs_args;
|
||||
let mut cmdline = vec![
|
||||
"console=ttyS0,115200",
|
||||
"rw",
|
||||
"earlyprintk=serial",
|
||||
"nokaslr",
|
||||
&init_arg,
|
||||
"devtmpfs.mount=1", // Automatically mount /dev at boot
|
||||
let mut cmdline: Vec<String> = vec![
|
||||
"console=ttyS0,115200".into(),
|
||||
"rw".into(),
|
||||
"earlyprintk=serial".into(),
|
||||
"nokaslr".into(),
|
||||
"init=/slim/init".into(),
|
||||
"devtmpfs.mount=1".into(), // Automatically mount /dev at boot
|
||||
];
|
||||
|
||||
let fs_args;
|
||||
let qcow2_arg = format!("file={},format=qcow2,if=virtio", qcow2_path.display());
|
||||
if qcow2_path.exists() {
|
||||
fs_args = vec!["-drive", &qcow2_arg];
|
||||
cmdline.extend_from_slice(&["root=/dev/vda", "rootfstype=ext4"]);
|
||||
fs_args = vec!["-drive".to_string(), qcow2_arg];
|
||||
cmdline.extend(["root=/dev/vda".to_string(), "rootfstype=ext4".to_string()]);
|
||||
} else if initrd_path.exists() {
|
||||
fs_args = vec!["-initrd", initrd_path.to_str().context("Invalid UTF-8")?];
|
||||
cmdline.push("root=/dev/ram0");
|
||||
let initrd_str = initrd_path.to_str().context("Invalid UTF-8")?.to_string();
|
||||
fs_args = vec!["-initrd".to_string(), initrd_str];
|
||||
cmdline.push("root=/dev/ram0".into());
|
||||
} else {
|
||||
bail!("Registry missing rootfs/initrd for '{name}'. Run `slim build` first.");
|
||||
}
|
||||
|
||||
if let Some(cmd) = &cmd {
|
||||
let encoded = base64::engine::general_purpose::STANDARD.encode(cmd.as_bytes());
|
||||
cmdline.push(format!("slim.cmd={encoded}"));
|
||||
}
|
||||
|
||||
println!("Booting {name} from registry: {}", reg_dir.display());
|
||||
println!(
|
||||
" qemu-system-x86_64 -m 256 -nographic -kernel {} -initrd {} -append 'console=ttyS0'",
|
||||
vmlinuz_path.display(),
|
||||
initrd_path.display()
|
||||
);
|
||||
|
||||
let cmdline = cmdline.join(" ");
|
||||
|
||||
@@ -90,8 +100,9 @@ pub(crate) fn run(
|
||||
.args(["-m", &memory])
|
||||
.arg("-kernel")
|
||||
.arg(vmlinuz_path.as_os_str())
|
||||
.args(fs_args)
|
||||
.args(&fs_args)
|
||||
.args(["-snapshot"])
|
||||
.args(["-no-reboot"])
|
||||
.args(["-append", &cmdline])
|
||||
.args(["-nographic"])
|
||||
.args(["-nic", &network]);
|
||||
|
||||
Reference in New Issue
Block a user