Skip to content

feat(darwin): Pass an ordered list of disks to the VMM - #1094

Draft
ananos wants to merge 1 commit into
feat/initrd-hvi-backend-v0.8.0from
feat/multi-disk-darwin
Draft

ananos wants to merge 1 commit into
feat/initrd-hvi-backend-v0.8.0from
feat/multi-disk-darwin

Conversation

@ananos

@ananos ananos commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Description

hull is moving container roots on macOS to an overlay of a read-only
image disk and a per-instance writable disk, with virtio-fs left for the
home share. That needs more than one block device per guest, and
ExecArgs has a single BlockDevPath.

This PR adds ExecArgs.BlockDevs []BlockDevSpec{Path, ReadOnly} and
EffectiveBlockDevs(), which returns BlockDevs when set and otherwise
BlockDevPath as one writable disk. The darwin hvi builder emits
--disk / --disk-ro per entry, in order. The darwin vz builder emits
them after the root share and no longer drops Sharedfs when block
devices are present; --rootfs stays the legacy single-root path and is
only used when BlockDevs is empty. Both builders refuse an empty or a
duplicate path. The Linux hvi and the QEMU builders still read
BlockDevPath only.

docs/vz-runner-contract.md documents the new flags and the serial rule
(disk<N> counted over --disk* only).

Related issues

How was this tested?

Unit tests in hvi_darwin_test.go and vz_darwin_test.go pin the exact
flag sequence for the legacy path, two disks (ro then rw), disks beside a
share, the RootfsPath share with disks, and the empty/duplicate
rejections; the new cases fail against the old builders. go build ./..., go test ./pkg/unikontainers/... and GOOS=linux go vet ./pkg/unikontainers/... pass. End to end through hull on this Mac: a
guest boots with overlay / over disk0 (ro) and disk1 on both hvi
and vz, with the home share mounted on top.

LLM usage

Claude Code (Opus 4.8 for the implementation, a second model for
review). Review pending on my side.

Checklist

  • I have read the contribution guide.
  • The linter passes locally (make lint). golangci-lint is clean
    on the changed packages; the two unused findings are in files this PR
    does not touch.
  • The e2e tests of at least one tool pass locally (make test_ctr, make test_nerdctl, make test_docker, make test_crictl). Not run; darwin-only change.
  • If LLMs were used: I have read the llm policy.

🤖 Generated with Claude Code

An overlay root on a block device needs two disks: a read-only lower
built from the image and a writable upper per instance. ExecArgs had a
single BlockDevPath, so the darwin builders could attach one disk only.
On vz that disk also replaced the root share.

ExecArgs gains BlockDevs, a list of BlockDevSpec{Path, ReadOnly} in
command-line order. The VMM gives each disk the serial disk<N> by its
position, and the guest finds its disks by serial. BlockDevPath stays
as the single-disk form. It counts as one writable disk when BlockDevs
is empty. EffectiveBlockDevs returns either form as a list. Only the
darwin builders read BlockDevs; the Linux hvi and the QEMU builders
still read BlockDevPath.

- hvi: emit --disk or --disk-ro per entry, in order. BlockDevPath alone
  still gives one --disk.
- vz: BlockDevPath alone still goes out as --rootfs and replaces the
  root share. Entries in BlockDevs go out as --disk or --disk-ro after
  the root share. The root share stays on the command line, both the
  Sharedfs one and the RootfsPath one.
- Both builders reject an entry with no path and a path listed twice,
  as they do for shares. A missing or repeated disk would shift the
  serials of the disks after it.
- docs/vz-runner-contract.md: document --disk, --disk-ro and the
  disk<N> serial, counted over those two flags only. Add the missing
  --share-ro row.

The Linux hvi builder is unchanged.

TestHviDarwinDisks and TestVzDarwinDisks cover the legacy path and a
read-only lower followed by a writable upper. The *DisksRejected tests
cover the empty and duplicate path errors on both builders.
TestVzDarwinDisksKeepRootShare and TestVzDarwinDisksKeepRootfsPathShare
check that BlockDevs keep the root share on vz, and that BlockDevPath
still drops it. The BlockDevs cases fail against the previous builders.

Signed-off-by: Anastassios Nanos <ananos@nofire.ai>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant