feat(minvmd): scaffold crate with libkrun FFI wrappers and smoke test - #237
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (13)
✅ Files skipped from review due to trivial changes (3)
🚧 Files skipped from review as they are similar to previous changes (10)
📝 WalkthroughWalkthroughAdds a new macOS-only minvmd crate: workspace registration, crate manifest and build script for linking libkrun, entitlements and codesign recipe, raw FFI and check_backend, a safe Context wrapper, CLI skeleton, and macOS-only smoke tests. Changesminvmd crate—macOS hypervisor integration with libkrun
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
4c941fb to
d500918
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/minvmd/src/krun/ctx.rs (1)
93-106: ⚡ Quick winPrefer Option combinators over reconstructive
matchblocks.The
envp_cstrs,initramfs_cstr, andcmdline_cstrconversions can be expressed more idiomatically with combinators, which keeps the flow tighter and aligns with repo Rust style.Refactor sketch
- let envp_cstrs: Option<Vec<CString>> = match envp { - Some(entries) => Some(cstrings_from_strs(entries, "envp")?), - None => None, - }; + let envp_cstrs = envp + .map(|entries| cstrings_from_strs(entries, "envp")) + .transpose()?; ... - let initramfs_cstr = match initramfs { - Some(p) => Some(cstring_from_path(p.as_ref(), "initramfs")?), - None => None, - }; - let cmdline_cstr = match cmdline { - Some(s) => Some(cstring_from_str(s, "cmdline")?), - None => None, - }; + let initramfs_cstr = initramfs + .map(|p| cstring_from_path(p.as_ref(), "initramfs")) + .transpose()?; + let cmdline_cstr = cmdline + .map(|s| cstring_from_str(s, "cmdline")) + .transpose()?;As per coding guidelines, "Use combinators on Option/Result (map, and_then, ok_or, unwrap_or_else, map_err) instead of match expressions that reconstruct the same enum".
Also applies to: 148-155
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/minvmd/src/krun/ctx.rs` around lines 93 - 106, The envp conversion reconstructs Option with match; replace it with Option combinators: build envp_cstrs by using envp.map(|entries| cstrings_from_strs(entries, "envp")?) (or map_then/and_then to propagate Result), and then create (envp_ptrs, envp_ptr) with envp_cstrs.as_ref().map(|cstrs| { let mut ptrs = cstrs.iter().map(|c| c.as_ptr()).collect::<Vec<_>>(); ptrs.push(ptr::null()); (ptrs, ptrs.as_ptr() as *const *const c_char) }).unwrap_or((Vec::new(), ptr::null())); apply the same pattern to initramfs_cstr and cmdline_cstr conversions to remove reconstructive match blocks while preserving the existing symbols envp_cstrs, cstrings_from_strs, envp_ptrs, and envp_ptr.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/minvmd/src/krun/ctx.rs`:
- Around line 93-106: The envp conversion reconstructs Option with match;
replace it with Option combinators: build envp_cstrs by using envp.map(|entries|
cstrings_from_strs(entries, "envp")?) (or map_then/and_then to propagate
Result), and then create (envp_ptrs, envp_ptr) with
envp_cstrs.as_ref().map(|cstrs| { let mut ptrs = cstrs.iter().map(|c|
c.as_ptr()).collect::<Vec<_>>(); ptrs.push(ptr::null()); (ptrs, ptrs.as_ptr() as
*const *const c_char) }).unwrap_or((Vec::new(), ptr::null())); apply the same
pattern to initramfs_cstr and cmdline_cstr conversions to remove reconstructive
match blocks while preserving the existing symbols envp_cstrs,
cstrings_from_strs, envp_ptrs, and envp_ptr.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 27eca8fd-0dc8-44c6-af37-08383bf7c5f8
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (13)
Cargo.tomlcrates/minvmd/Cargo.tomlcrates/minvmd/build.rscrates/minvmd/minvmd.entitlementscrates/minvmd/src/bin/krun_smoke_child.rscrates/minvmd/src/error.rscrates/minvmd/src/krun/ctx.rscrates/minvmd/src/krun/mod.rscrates/minvmd/src/krun/raw.rscrates/minvmd/src/lib.rscrates/minvmd/src/main.rscrates/minvmd/tests/krun_smoke.rsjustfile
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/minvmd/src/krun/mod.rs (1)
17-18: ⚡ Quick winNarrow module visibility to avoid exposing internal layering details.
Line 17 and Line 18 currently publish internals (
ctx,raw) as part of the crate API. Prefer keeping modules private (orpub(crate)) and exposing only curated items likeContextvia re-export.♻️ Suggested visibility tightening
-pub mod ctx; -pub mod raw; +mod ctx; +pub(crate) mod raw; pub use ctx::Context;As per coding guidelines, "Make items private by default; widen to
pub(crate)beforepub; curate the public API viapub useat the crate root".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/minvmd/src/krun/mod.rs` around lines 17 - 18, The crate currently exposes internal modules `ctx` and `raw` publicly; change their visibility to private (or `pub(crate)`) so internals aren't part of the public API, then re-export only the curated public types (e.g., `Context`) from the crate root using `pub use` (reference the `ctx` module's `Context` type and any other public-facing symbols you intend to expose) so consumers see only the intended API surface while implementation details in `ctx` and `raw` remain hidden.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/minvmd/src/krun/mod.rs`:
- Around line 17-18: The crate currently exposes internal modules `ctx` and
`raw` publicly; change their visibility to private (or `pub(crate)`) so
internals aren't part of the public API, then re-export only the curated public
types (e.g., `Context`) from the crate root using `pub use` (reference the `ctx`
module's `Context` type and any other public-facing symbols you intend to
expose) so consumers see only the intended API surface while implementation
details in `ctx` and `raw` remain hidden.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 91e08578-ca0e-47f6-b41a-31e7371f5d24
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (13)
Cargo.tomlcrates/minvmd/Cargo.tomlcrates/minvmd/build.rscrates/minvmd/minvmd.entitlementscrates/minvmd/src/bin/krun_smoke_child.rscrates/minvmd/src/error.rscrates/minvmd/src/krun/ctx.rscrates/minvmd/src/krun/mod.rscrates/minvmd/src/krun/raw.rscrates/minvmd/src/lib.rscrates/minvmd/src/main.rscrates/minvmd/tests/krun_smoke.rsjustfile
✅ Files skipped from review due to trivial changes (2)
- crates/minvmd/minvmd.entitlements
- Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (10)
- crates/minvmd/Cargo.toml
- justfile
- crates/minvmd/build.rs
- crates/minvmd/src/main.rs
- crates/minvmd/src/lib.rs
- crates/minvmd/src/error.rs
- crates/minvmd/src/bin/krun_smoke_child.rs
- crates/minvmd/src/krun/ctx.rs
- crates/minvmd/src/krun/raw.rs
- crates/minvmd/tests/krun_smoke.rs
twitchyliquid64
left a comment
There was a problem hiding this comment.
Did we try using the libkrun crate? would prefer that rather than rolling our own FFI if that works
https://crates.io/crates/libkrun corresponding to https://github.com/containers/libkrun/blob/main/src/libkrun/src/lib.rs
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@evanspearman thanks for the feedback. For now I am going to close this PR in favour of a krunkit exploration spike. I will incorporate your feedback for future PRs. |
Adds the macOS-only `minvmd` crate that will, in follow-up changes, bring up a
Linux microVM via libkrun, supervise its lifecycle, and bridge a host UDS to
a guest vsock port. This change lands the bones only: crate scaffold, FFI
surface, safe wrappers, and a gated smoke test. Boot, lifecycle, and bridge
work follows.
- New `crates/minvmd` workspace member; `krun` module is
`#[cfg(target_os = "macos")]`-gated and Linux ships only a runtime-bailing
stub so the existing Linux-only CI stays green.
- `build.rs` emits `cargo:rustc-link-search` and `cargo:rustc-link-arg=-Wl,-rpath`
on macOS, defaulting the libkrun prefix to `/opt/homebrew/lib` with a
`LIBKRUN_PREFIX` env override.
- libkrun FFI declarations in `src/krun/raw.rs` cover the smoke-test surface
(create_ctx, set_vm_config, set_exec, start_enter, free_ctx) and the
forward-looking boot surface (set_root, set_kernel, add_vsock_port,
set_console_output). A single block-level `// SAFETY:` enumerates the
pointer / NUL-termination / ownership-transfer invariants the `extern "C"`
block inherits from the C ABI; per-function doc comments describe each
function's contract.
- Safe wrappers in `src/krun/ctx.rs` expose an RAII `Context` (non-Clone /
non-Copy, Drop calls `krun_free_ctx`) that validates inputs in safe Rust
(NUL-termination via `CString`) before any FFI call. Every unsafe call
site carries its own `// SAFETY:` comment naming the invariants the
wrapper enforces. `start_enter` consumes via `mem::forget` because libkrun
documents the configuration as consumed unconditionally.
- Typed `VmError` matches the hand-rolled error style used by sandbox2:
`Backend { op, code }` preserves errno magnitude; `NulInPath` / `NulInString`
cover boundary validation; `StartEnterReturnedUnexpectedly { ret }`
surfaces the libkrun-docs-violating path explicitly rather than synthesising
a misleading "errno 0".
- `minvmd.entitlements` grants only `com.apple.security.hypervisor`;
`justfile` `codesign-minvmd` recipe builds release and ad-hoc-signs.
- `tests/krun_smoke.rs` is `#[ignore]` and self-skips unless `MINVMD_E2E=1`.
When opted in it spawns the auto-discovered `src/bin/krun_smoke_child`
helper bin (separate process because `krun_start_enter` `exit()`s on
success and never returns) which drives the safe wrappers end-to-end.
Verified end-to-end against libkrun v1.18.1 on `aarch64-apple-darwin`.
Verification:
cargo fmt && cargo clippy -p minvmd --all-targets -- -D warnings → clean
cargo test -p minvmd → 9 passed
MINVMD_E2E=1 cargo test -p minvmd --test krun_smoke -- --include-ignored
on macOS with libkrun v1.18.1 installed → passed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8767e45 to
f868ce4
Compare
|
@twitchyliquid64 on the The canonical Evan's review points are all addressed in the latest push; threads replied inline. |
f868ce4 to
b585a67
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/minvmd/src/krun/raw.rs (1)
46-47: ⚡ Quick winStale reference:
check_backendis no longer onVmError.This SAFETY note still points at
crate::error::VmError::check_backend, but the helper was moved into this module as a free function. Update the reference to avoid misleading future contributors.📝 Proposed doc fix
-// - Return values are i32: zero or positive on success, negative errno on -// failure. [`crate::error::VmError::check_backend`] translates these. +// - Return values are i32: zero or positive on success, negative errno on +// failure. [`check_backend`] translates these.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/minvmd/src/krun/raw.rs` around lines 46 - 47, The SAFETY doc comment refers to the moved helper as crate::error::VmError::check_backend, which is stale; update the reference to the new free function name in this module (check_backend) or fully qualify it (crate::krun::raw::check_backend) in the comment so readers can find the helper; adjust the SAFETY note text to mention check_backend as a free function rather than a VmError method.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/minvmd/src/krun/raw.rs`:
- Around line 46-47: The SAFETY doc comment refers to the moved helper as
crate::error::VmError::check_backend, which is stale; update the reference to
the new free function name in this module (check_backend) or fully qualify it
(crate::krun::raw::check_backend) in the comment so readers can find the helper;
adjust the SAFETY note text to mention check_backend as a free function rather
than a VmError method.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 464259da-a161-4455-b1f5-6d03b8e4e515
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (13)
Cargo.tomlcrates/minvmd/Cargo.tomlcrates/minvmd/build.rscrates/minvmd/minvmd.entitlementscrates/minvmd/src/bin/krun_smoke_child.rscrates/minvmd/src/error.rscrates/minvmd/src/krun/ctx.rscrates/minvmd/src/krun/mod.rscrates/minvmd/src/krun/raw.rscrates/minvmd/src/lib.rscrates/minvmd/src/main.rscrates/minvmd/tests/krun_smoke.rsjustfile
✅ Files skipped from review due to trivial changes (2)
- crates/minvmd/Cargo.toml
- Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (8)
- justfile
- crates/minvmd/src/main.rs
- crates/minvmd/src/lib.rs
- crates/minvmd/minvmd.entitlements
- crates/minvmd/src/krun/mod.rs
- crates/minvmd/src/krun/ctx.rs
- crates/minvmd/build.rs
- crates/minvmd/src/bin/krun_smoke_child.rs
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
CodeRabbit nitpick: prefer "private by default; widen to pub(crate) before pub;
curate the public API via pub use at the crate root" per the workspace coding
guideline.
- src/krun/mod.rs: ctx and raw are now `mod`, not `pub mod`. Within the same
module they remain accessible to siblings, so ctx.rs's `use crate::krun::raw`
still resolves.
- Re-export the two libkrun kernel-format constants the test helper actually
needs (KRUN_KERNEL_FORMAT_IMAGE_BZ2, KRUN_KERNEL_FORMAT_IMAGE_GZ) at the
krun root.
- Update krun_smoke_child.rs to import via the curated path
`minvmd::krun::{Context, KRUN_KERNEL_FORMAT_*}`.
- Drop the three unused constants (RAW, ELF, PE_GZ) that were only public
surface; add a comment naming them as added-on-demand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`VmError::check_backend` translates libkrun return codes and has callers only
inside the `krun` module, which is itself `#[cfg(target_os = "macos")]`-gated.
On Linux the krun module compiles out, leaving check_backend with no callers
in the lib target — clippy's `-D dead-code` then fails the Linux CI job.
Gate `impl VmError { check_backend }` on `target_os = "macos"`, and gate the
two unit tests that exercise it. The Display + variant tests stay portable.
Verified:
cargo clippy -p minvmd --all-targets -- -D warnings → clean
cargo clippy --target aarch64-unknown-linux-musl -p minvmd \
--all-targets -- -D warnings → clean
cargo test -p minvmd → 9 passed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- VmError::Backend now carries io::Error (built via io::Error::from_raw_os_error(checked_neg)) instead of a bare i32 code, preserving the errno and wiring up Error::source(). - Move check_backend off VmError into the krun FFI module as a free function; drop the no-op #[inline] and the scattered #[cfg(macos)] (the helper now lives entirely in the macOS-only krun module). - krun_smoke_child propagates errors with ? via Result-returning main and run_macos instead of expect(). - Express the envp/initramfs/cmdline Option conversions with map().transpose() per the repo's combinator standard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
b585a67 to
c0aed34
Compare
Summary
Lands the macOS-only
minvmdcrate as a scaffold + FFI surface + gated smoke test. Subsequent PRs add VM boot, the UDS↔vsock bridge, and the lifecycle daemon.crates/minvmdworkspace member;krunmodule is#[cfg(target_os = "macos")]-gated. Linux ships only a runtime-bailing stub so existing Linux-only CI stays green.build.rsemits link search + rpath on macOS, defaulting to/opt/homebrew/libwith aLIBKRUN_PREFIXenv override.src/krun/raw.rscovers the smoke-test surface (create_ctx,set_vm_config,set_exec,start_enter,free_ctx) plus the forward boot surface (set_root,set_kernel,add_vsock_port,set_console_output). One block-level// SAFETY:enumerates the pointer / NUL-termination / ownership-transfer invariants theextern \"C\"block inherits from the C ABI; per-function doc comments describe each function's contract.src/krun/ctx.rsexpose an RAIIContext(non-Clone / non-Copy,Dropcallskrun_free_ctx) that validates inputs in safe Rust (NUL-termination viaCString). Every unsafe call site carries its own// SAFETY:comment naming the invariants the wrapper enforces.start_enterconsumes viamem::forgetbecause libkrun documents the configuration as consumed unconditionally.VmErrormatches the hand-rolled style insandbox2:Backend { op, code }preserves errno magnitude;NulInPath/NulInStringcover boundary validation;StartEnterReturnedUnexpectedly { ret }surfaces the libkrun-docs-violating path explicitly instead of synthesising a misleading "errno 0".minvmd.entitlementsgrants onlycom.apple.security.hypervisor.justfilecodesign-minvmdrecipe builds release and ad-hoc-signs.tests/krun_smoke.rsis#[ignore]and self-skips unlessMINVMD_E2E=1. When opted in it spawns the auto-discoveredsrc/bin/krun_smoke_childhelper (separate process becausekrun_start_enterexit()s on success and never returns) which drives the safe wrappers end-to-end.Out of scope (intentional)
minimald(follow-up).minimal2) (follow-up).set_kernel,set_root,add_vsock_port,set_console_output) is declared and wrapped but not yet exercised.Test plan
cargo build -p minvmd— green on aarch64-apple-darwincargo clippy -p minvmd --all-targets -- -D warnings— no issuescargo fmt --check— cleancargo test -p minvmd— 9 passed, 1 ignored (the gated smoke test)MINVMD_E2E=1 cargo test -p minvmd --test krun_smoke -- --include-ignored— 1 passed against real libkrun v1.18.1 on macOSkrunmodule is gated out, no libkrun linkage attemptedjust codesign-minvmdon a Mac — produces a release binary signed with the hypervisor entitlement🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Libraries / Runtime
Tests
Chores