|
| 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 | +functionfind_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 | +forcandidatein"${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 | +functionsetup() { |
| 30 | +INITRAMFS_ROOT="$(mktemp -d"$BATS_RUN_TMPDIR/runc-initramfs.XXXXXX")" |
| 31 | +find_vmlinuz |
| 32 | +} |
| 33 | + |
| 34 | +functionteardown() { |
| 35 | +[ -v INITRAMFS_ROOT ]&& rm -rf"$INITRAMFS_ROOT" |
| 36 | +} |
| 37 | + |
| 38 | +functionqemu_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 | +forcandidatein"${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" -machinehelp>&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 |
| 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 | +} |