|
| 1 | +#!/usr/bin/env bats |
| 2 | + |
| 3 | +load helpers |
| 4 | + |
| 5 | +# Rather than building our own kernel for use with qemu, just reuse the host's |
| 6 | +# kernel since we just need some kernel that supports containers that we can |
| 7 | +# use to run our custom initramfs. |
| 8 | +function find_vmlinuz() { |
| 9 | + shopt -s nullglob |
| 10 | + local candidate candidates=( |
| 11 | + /boot/vmlinuz |
| 12 | + /boot/vmlinuz-"$(uname -r)"* |
| 13 | + /usr/lib*/modules/"$(uname -r)"/vmlinuz* |
| 14 | + ) |
| 15 | + shopt -u nullglob |
| 16 | + |
| 17 | + for candidate in "${candidates[@]}"; do |
| 18 | + [ -e "$candidate" ] || continue |
| 19 | + export HOST_KERNEL="$candidate" |
| 20 | + return 0 |
| 21 | + done |
| 22 | + |
| 23 | + # Actuated doesn't provide a copy of the boot kernel, so we have to skip |
| 24 | + # the test in that case. It also seems they don't allow aarch64 guests |
| 25 | + # either (see <https://docs.actuated.com/examples/kvm-guest/>). |
| 26 | + skip "could not find host vmlinuz kernel" |
| 27 | +} |
| 28 | + |
| 29 | +function setup() { |
| 30 | + INITRAMFS_ROOT="$(mktemp -d "$BATS_RUN_TMPDIR/runc-initramfs.XXXXXX")" |
| 31 | + find_vmlinuz |
| 32 | +} |
| 33 | + |
| 34 | +function teardown() { |
| 35 | + [ -v INITRAMFS_ROOT ] && rm -rf "$INITRAMFS_ROOT" |
| 36 | +} |
| 37 | + |
| 38 | +function qemu_native() { |
| 39 | + # Different distributions put qemu-kvm in different locations and with |
| 40 | + # different names. Debian and Ubuntu have a "kvm" binary, while AlmaLinux |
| 41 | + # has /usr/libexec/qemu-kvm. |
| 42 | + local qemu_binary="" qemu_candidates=("kvm" "qemu-kvm" "/usr/libexec/qemu-kvm") |
| 43 | + local candidate |
| 44 | + for candidate in "${qemu_candidates[@]}"; do |
| 45 | + "$candidate" -help &>/dev/null || continue |
| 46 | + qemu_binary="$candidate" |
| 47 | + break |
| 48 | + done |
| 49 | + # TODO: Maybe we should also try to call qemu-system-FOO for the current |
| 50 | + # architecture if qemu-kvm is missing? |
| 51 | + [ -n "$qemu_binary" ] || skip "could not find qemu-kvm binary" |
| 52 | + |
| 53 | + local machine= |
| 54 | + case "$(go env GOARCH)" in |
| 55 | + 386 | amd64) |
| 56 | + # Try to use a slightly newer PC CPU. |
| 57 | + machine="pc" |
| 58 | + ;; |
| 59 | + arm | arm64) |
| 60 | + # ARM doesn't provide a "default" machine value (because its use is so |
| 61 | + # varied) so we have to specify the machine manually. |
| 62 | + machine="virt" |
| 63 | + ;; |
| 64 | + *) |
| 65 | + echo "could not figure out -machine argument for qemu -- using default" >&2 |
| 66 | + ;; |
| 67 | + esac |
| 68 | + # We use -cpu max to ensure that the glibc we built runc with doesn't rely |
| 69 | + # on CPU features that the default QEMU CPU doesn't support (such as on |
| 70 | + # AlmaLinux 9). |
| 71 | + local machine_args=("-cpu" "max") |
| 72 | + [ -n "$machine" ] && machine_args+=("-machine" "$machine") |
| 73 | + |
| 74 | + sane_run --timeout=3m \ |
| 75 | + "$qemu_binary" "${machine_args[@]}" "$@" |
| 76 | + if [ "$status" -ne 0 ]; then |
| 77 | + # To help with debugging, output the set of valid machine values. |
| 78 | + "$qemu_binary" -machine help >&2 |
| 79 | + fi |
| 80 | +} |
| 81 | + |
| 82 | +@test "runc run [initramfs + pivot_root]" { |
| 83 | + requires root |
| 84 | + |
| 85 | + # Configure our minimal initrd. |
| 86 | + mkdir -p "$INITRAMFS_ROOT/initrd" |
| 87 | + pushd "$INITRAMFS_ROOT/initrd" |
| 88 | + |
| 89 | + # Use busybox as a base for our initrd. |
| 90 | + tar --exclude './dev/*' -xf "$BUSYBOX_IMAGE" |
| 91 | + # Make sure that "sh" and "poweroff" are installed, otherwise qemu will |
| 92 | + # boot loop when init stops. |
| 93 | + [ -x ./bin/sh ] || skip "busybox image is missing /bin/sh" |
| 94 | + [ -x ./bin/poweroff ] || skip "busybox image is missing /bin/poweroff" |
| 95 | + |
| 96 | + # Copy the runc binary into the container. In theory we would prefer to |
| 97 | + # copy a static binary, but some distros (like openSUSE) don't ship |
| 98 | + # libseccomp-static so requiring a static build for any integration test |
| 99 | + # run wouldn't work. Instead, we copy all of the library dependencies into |
| 100 | + # the rootfs (note that we also have to copy ld-linux-*.so because runc was |
| 101 | + # probably built with a newer glibc than the one in our busybox image. |
| 102 | + cp "$RUNC" ./bin/runc |
| 103 | + readarray -t runclibs \ |
| 104 | + <<<"$(ldd "$RUNC" | grep -Eo '/[^ ]*lib[^ ]*.so.[^ ]*')" |
| 105 | + cp -vt ./lib64/ "${runclibs[@]}" |
| 106 | + # busybox has /lib64 -> /lib so we can just fill in one path. |
| 107 | + |
| 108 | + # Create a container bundle using the same busybox image. |
| 109 | + mkdir -p ./run/bundle |
| 110 | + pushd ./run/bundle |
| 111 | + mkdir -p rootfs |
| 112 | + tar --exclude './dev/*' -C rootfs -xf "$BUSYBOX_IMAGE" |
| 113 | + runc spec |
| 114 | + update_config '.process.args = ["/bin/echo", "hello from inside the container"]' |
| 115 | + popd |
| 116 | + |
| 117 | + # Build a custom /init script. |
| 118 | + cat >./init <<-EOF |
| 119 | + #!/bin/sh |
| 120 | +
|
| 121 | + set -x |
| 122 | + echo "==START INIT SCRIPT==" |
| 123 | +
|
| 124 | + mkdir -p /proc /sys |
| 125 | + mount -t proc proc /proc |
| 126 | + mkdir -p /sys |
| 127 | + mount -t sysfs sysfs /sys |
| 128 | +
|
| 129 | + mkdir -p /sys/fs/cgroup |
| 130 | + mount -t cgroup2 cgroup2 /sys/fs/cgroup |
| 131 | +
|
| 132 | + mkdir -p /tmp |
| 133 | + mount -t tmpfs tmpfs /tmp |
| 134 | +
|
| 135 | + mkdir -p /dev |
| 136 | + mount -t devtmpfs devtmpfs /dev |
| 137 | + mkdir -p /dev/pts |
| 138 | + mount -t devpts -o newinstance devpts /dev/pts |
| 139 | + mkdir -p /dev/shm |
| 140 | + mount --bind /tmp /dev/shm |
| 141 | +
|
| 142 | + # Wait for as little as possible if we panic so we can output the error |
| 143 | + # log as part of the test failure before the test times out. |
| 144 | + echo 1 >/proc/sys/kernel/panic |
| 145 | +
|
| 146 | + runc run -b /run/bundle ctr |
| 147 | +
|
| 148 | + echo "==END INIT SCRIPT==" |
| 149 | + poweroff -f |
| 150 | + EOF |
| 151 | + chmod +x ./init |
| 152 | + |
| 153 | + find . | cpio -o -H newc >"$INITRAMFS_ROOT/initrd.cpio" |
| 154 | + popd |
| 155 | + |
| 156 | + # Now we can just run the image (use qemu-kvm so that we run on the same |
| 157 | + # architecture as the host system). We can just reuse the host kernel. |
| 158 | + qemu_native \ |
| 159 | + -initrd "$INITRAMFS_ROOT/initrd.cpio" \ |
| 160 | + -kernel "$HOST_KERNEL" \ |
| 161 | + -m 512M \ |
| 162 | + -nographic -append console=ttyS0 -no-reboot |
| 163 | + [ "$status" -eq 0 ] |
| 164 | + [[ "$output" = *"==START INIT SCRIPT=="* ]] |
| 165 | + [[ "$output" = *"hello from inside the container"* ]] |
| 166 | + [[ "$output" = *"==END INIT SCRIPT=="* ]] |
| 167 | +} |
0 commit comments