Description
A Linux container on cloud-hypervisor never starts if it has more than one block device.
CloudHypervisor.BuildExecCmd adds a separate --disk flag per block device:
https://github.com/urunc-dev/urunc/blob/main/pkg/unikontainers/hypervisors/cloud_hypervisor.go#L111-L121
But cloud-hypervisor expects all disks after a single --disk. Repeating the flag is a
hard error, not an override:
error: the argument '--disk <disk>...' cannot be used multiple times
So a block rootfs plus one block-backed volume produces two flags and the VM is never
created. Nothing filters the block list per monitor, so N devices always give N flags
(blockRootfs.getBlockDevs() -> monitorResources.BlockArgs -> Linux.MonitorBlockCli()
cloud-hypervisor case).
A single disk works fine, which is probably why this hasn't come up: all four
cloud-hypervisor e2e cases use one disk.
There the two --initramfs appends could never both fire.
Here they always do
QEMU and firecracker aren't affected (qemu concatenates into one string, firecracker
builds a JSON drives array)
System info
- Urunc version: 3395292 (main)
- Arch: x86_64, kernel 6.6.87.2-microsoft-standard-WSL2
- VMM: cloud-hypervisor v50.0.0 (same version pinned in ci.yml)
- Unikernel: linux
Steps to reproduce
No containerd, docker or devmapper needed. As root:
-
CGO_ENABLED=1 go build -o /root/urunc ./cmd/urunc, install cloud-hypervisor v50.0
-
Loop-backed ext4 filesystem, which becomes the second disk:
truncate -s 64M data.img && mkfs.ext4 -q -F data.img
mount -o loop data.img /root/uruncdemo/datamnt
-
Bundle with rootfs.img and kernel in its rootfs, plus:
"mounts": [{"destination":"/data","type":"bind",
"source":"/root/uruncdemo/datamnt","options":["rbind","rw"]}],
"annotations": {
"com.urunc.unikernel.unikernelType": "linux",
"com.urunc.unikernel.hypervisor": "cloud-hypervisor",
"com.urunc.unikernel.binary": "/kernel",
"com.urunc.unikernel.block": "/rootfs.img",
"com.urunc.unikernel.blkMntPoint": "/"
}
-
urunc --debug --root /run/urunc create -b <bundle> demoA, then urunc --root /run/urunc start demoA
-
Repeat with "mounts": [] as a control
I used the block annotation instead of devmapper to get a block rootfs. Both end up in
the same blockRootfs builder and generate the same argv
Logs / output
Two disks:
command="[/usr/local/bin/cloud-hypervisor --memory size=268M --cpus boot=1
--kernel /kernel --console off --serial tty --seccomp false
--disk path=/rootfs.img,id=rootfs --disk path=/dev/loop0,id=vol0
--cmdline panic=-1 console=ttyS0 root=/dev/vda rw ... init=/sbin/init -- ]"
error: the argument '--disk <disk>...' cannot be used multiple times
Usage: cloud-hypervisor [OPTIONS]
Same container with the volume removed, one disk:
Fatal error: VmBoot(VmBoot(KernelLoad(Bzimage(InvalidBzImage))))
0: Error booting VM
1: The VM could not boot
2: Cannot load the kernel into memory
3: Kernel Loader: failed to load bzImage kernel image
With one disk it clears argument parsing and only stops on my placeholder /kernel
The argv is built and rejected before the kernel is
read, so the placeholder isn't what breaks the two-disk case. Full logs attached.
Possible fix
Collect the specs and emit one flag:
var disks []string
for _, blockArg := range blockArgs { ...; disks = append(disks, diskArg) }
if len(disks) > 0 {
exArgs = append(exArgs, "--disk")
exArgs = append(exArgs, disks...)
}
Happy to send a patch if you want it.
There's no cloud_hypervisor_test.go right now, so
I could add one asserting a single --disk for N devices.
Related to #160.
Description
A Linux container on cloud-hypervisor never starts if it has more than one block device.
CloudHypervisor.BuildExecCmdadds a separate--diskflag per block device:https://github.com/urunc-dev/urunc/blob/main/pkg/unikontainers/hypervisors/cloud_hypervisor.go#L111-L121
But cloud-hypervisor expects all disks after a single
--disk. Repeating the flag is ahard error, not an override:
So a block rootfs plus one block-backed volume produces two flags and the VM is never
created. Nothing filters the block list per monitor, so N devices always give N flags
(
blockRootfs.getBlockDevs()->monitorResources.BlockArgs->Linux.MonitorBlockCli()cloud-hypervisor case).
A single disk works fine, which is probably why this hasn't come up: all four
cloud-hypervisor e2e cases use one disk.
There the two
--initramfsappends could never both fire.Here they always do
QEMU and firecracker aren't affected (qemu concatenates into one string, firecracker
builds a JSON drives array)
System info
Steps to reproduce
No containerd, docker or devmapper needed. As root:
CGO_ENABLED=1 go build -o /root/urunc ./cmd/urunc, install cloud-hypervisor v50.0Loop-backed ext4 filesystem, which becomes the second disk:
Bundle with
rootfs.imgandkernelin its rootfs, plus:urunc --debug --root /run/urunc create -b <bundle> demoA, thenurunc --root /run/urunc start demoARepeat with
"mounts": []as a controlI used the block annotation instead of devmapper to get a block rootfs. Both end up in
the same
blockRootfsbuilder and generate the same argvLogs / output
Two disks:
Same container with the volume removed, one disk:
With one disk it clears argument parsing and only stops on my placeholder
/kernelThe argv is built and rejected before the kernel is
read, so the placeholder isn't what breaks the two-disk case. Full logs attached.
Possible fix
Collect the specs and emit one flag:
Happy to send a patch if you want it.
There's no
cloud_hypervisor_test.goright now, soI could add one asserting a single
--diskfor N devices.Related to #160.