Skip to content

Commit 0dfe91b

Browse files
committed
tests: add test for pivot_root in initramfs support
The only way to run in the proper initramfs is to start a VM using a custom initrd that runs runc. This should be a fairly reasonable smoke-test that matches what minikube and kata do. Unfortunately, running the right qemu for the native architecture on various distros is a little different, so we need a helper function to get it to work on both Debian and AlmaLinux. Signed-off-by: Aleksa Sarai <[email protected]>
1 parent 562c89a commit 0dfe91b

File tree

6 files changed

+131
-6
lines changed

6 files changed

+131
-6
lines changed

.cirrus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ task:
7979
CIRRUS_WORKING_DIR: /home/runc
8080
GO_VERSION: "1.23"
8181
BATS_VERSION: "v1.9.0"
82-
RPMS: gcc git iptables jq glibc-static libseccomp-devel make criu fuse-sshfs container-selinux
82+
RPMS: gcc git iptables jq qemu-kvm glibc-static libseccomp-devel make criu fuse-sshfs container-selinux
8383
# yamllint disable rule:key-duplicates
8484
matrix:
8585
DISTRO: almalinux-8

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ jobs:
120120
- name: install deps
121121
run: |
122122
sudo apt update
123-
sudo apt -y install libseccomp-dev sshfs uidmap
123+
sudo apt -y install libseccomp-dev qemu-kvm sshfs uidmap
124124
125125
- name: install CRIU
126126
if: ${{ matrix.criu == '' }}

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \
1616
criu \
1717
gcc \
1818
gcc-multilib \
19+
cpio \
1920
curl \
2021
gawk \
2122
gperf \
@@ -24,6 +25,7 @@ RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \
2425
kmod \
2526
pkg-config \
2627
python3-minimal \
28+
qemu-system-x86 \
2729
sshfs \
2830
sudo \
2931
uidmap \

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ localunittest: all
172172
integration: runcimage
173173
$(CONTAINER_ENGINE) run $(CONTAINER_ENGINE_RUN_FLAGS) \
174174
-t --privileged --rm \
175+
-v /boot:/boot:ro \
175176
-v /lib/modules:/lib/modules:ro \
176177
-v $(CURDIR):/go/src/$(PROJECT) \
177178
$(RUNC_IMAGE) make localintegration TESTPATH="$(TESTPATH)"

tests/integration/helpers.bash

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ eval "$IMAGES"
1313
unset IMAGES
1414

1515
: "${RUNC:="${INTEGRATION_ROOT}/../../runc"}"
16+
: "${RUNC_STATIC:="${INTEGRATION_ROOT}/../../runc.static"}"
1617
RECVTTY="${INTEGRATION_ROOT}/../../tests/cmd/recvtty/recvtty"
1718
SD_HELPER="${INTEGRATION_ROOT}/../../tests/cmd/sd-helper/sd-helper"
1819
SECCOMP_AGENT="${INTEGRATION_ROOT}/../../tests/cmd/seccompagent/seccompagent"
@@ -39,18 +40,25 @@ ARCH=$(uname -m)
3940
# Seccomp agent socket.
4041
SECCCOMP_AGENT_SOCKET="$BATS_TMPDIR/seccomp-agent.sock"
4142

42-
# Wrapper for runc.
43-
function runc() {
44-
run __runc "$@"
43+
function sane_run() {
44+
local cmd="$1"
45+
shift
46+
47+
run "$cmd" "$@"
4548

4649
# Some debug information to make life easier. bats will only print it if the
4750
# test failed, in which case the output is useful.
4851
# shellcheck disable=SC2154
49-
echo "$(basename "$RUNC") $* (status=$status):" >&2
52+
echo "$cmd $* (status=$status):" >&2
5053
# shellcheck disable=SC2154
5154
echo "$output" >&2
5255
}
5356

57+
# Wrapper for runc.
58+
function runc() {
59+
sane_run __runc "$@"
60+
}
61+
5462
# Raw wrapper for runc.
5563
function __runc() {
5664
"$RUNC" ${RUNC_USE_SYSTEMD+--systemd-cgroup} \

tests/integration/initramfs.bats

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env bats
2+
3+
load helpers
4+
5+
function setup() {
6+
INITRAMFS_ROOT="$(mktemp -d "$BATS_RUN_TMPDIR/runc-initramfs.XXXXXX")"
7+
}
8+
9+
function teardown() {
10+
[ -v INITRAMFS_ROOT ] && rm -rf "$INITRAMFS_ROOT"
11+
}
12+
13+
function qemu_native() {
14+
# Different distributions put qemu-kvm in different locations and with
15+
# different names. Debian and Ubuntu have a "kvm" binary, while AlmaLinux
16+
# has /usr/libexec/qemu-kvm.
17+
qemu_candidates=( "kvm" "qemu-kvm" "/usr/libexec/qemu-kvm" )
18+
19+
qemu_binary=
20+
for candidate in "${qemu_candidates[@]}"; do
21+
"$candidate" -help &>/dev/null || continue
22+
qemu_binary="$candidate"
23+
break
24+
done
25+
# TODO: Maybe we should also try to call qemu-system-FOO for the current
26+
# architecture if qemu-kvm is missing?
27+
[ -n "$qemu_binary" ] || fail "could not find qemu binary"
28+
29+
sane_run "$qemu_binary" "$@"
30+
}
31+
32+
@test "runc run [initramfs + pivot_root]" {
33+
requires root
34+
35+
# Configure our minimal initrd.
36+
mkdir -p "$INITRAMFS_ROOT/initrd"
37+
pushd "$INITRAMFS_ROOT/initrd"
38+
39+
# Use busybox as a base for our initrd.
40+
tar --exclude './dev/*' -xf "$BUSYBOX_IMAGE"
41+
# Make sure that "sh" and "poweroff" are installed, otherwise qemu will
42+
# boot loop when init stops.
43+
[ -x ./bin/sh ] || fail "busybox image is missing /bin/sh"
44+
[ -x ./bin/poweroff ] || fail "busybox image is missing /bin/poweroff"
45+
46+
# Copy the runc binary into the container. In theory we would prefer to
47+
# copy a static binary, but some distros (like openSUSE) don't ship
48+
# libseccomp-static so requiring a static build for any integration test
49+
# run wouldn't work. Instead, we copy all of the library dependencies into
50+
# the rootfs (note that we also have to copy ld-linux-*.so because runc was
51+
# probably built with a newer glibc than the one in our busybox image.
52+
cp "$RUNC" ./bin/runc
53+
readarray -t runclibs \
54+
<<<"$(ldd "$RUNC" | grep -Eo '/[^ ]*lib[^ ]*.so.[^ ]*')"
55+
mkdir -p ./lib{,64}
56+
cp -vt ./lib/ "${runclibs[@]}"
57+
cp -vt ./lib64/ "${runclibs[@]}"
58+
59+
# Create a container bundle using the same busybox image.
60+
mkdir -p ./run/bundle
61+
pushd ./run/bundle
62+
mkdir -p rootfs
63+
tar --exclude './dev/*' -C rootfs -xf "$BUSYBOX_IMAGE"
64+
runc spec
65+
update_config '.process.args = ["/bin/echo", "hello from inside the container"]'
66+
popd
67+
68+
# Build a custom /init script.
69+
cat >./init <<-EOF
70+
#!/bin/sh
71+
72+
set -x
73+
echo "==START INIT SCRIPT=="
74+
75+
mkdir -p /proc /sys
76+
mount -t proc proc /proc
77+
mkdir -p /sys
78+
mount -t sysfs sysfs /sys
79+
80+
mkdir -p /sys/fs/cgroup
81+
mount -t cgroup2 cgroup2 /sys/fs/cgroup
82+
83+
mkdir -p /tmp
84+
mount -t tmpfs tmpfs /tmp
85+
86+
mkdir -p /dev
87+
mount -t devtmpfs devtmpfs /dev
88+
mkdir -p /dev/pts
89+
mount -t devpts -o newinstance devpts /dev/pts
90+
mkdir -p /dev/shm
91+
mount --bind /tmp /dev/shm
92+
93+
runc run -b /run/bundle ctr
94+
95+
echo "==END INIT SCRIPT=="
96+
poweroff -f
97+
EOF
98+
chmod +x ./init
99+
100+
find . | cpio -o -H newc >"$INITRAMFS_ROOT/initrd.cpio"
101+
popd
102+
103+
# Now we can just run the image (use qemu-kvm so that we run on the same
104+
# architecture as the host system). We can just reuse the host kernel.
105+
qemu_native \
106+
-initrd "$INITRAMFS_ROOT/initrd.cpio" \
107+
-kernel /boot/vmlinuz \
108+
-m 512M \
109+
-nographic -append console=ttyS0
110+
[ "$status" -eq 0 ]
111+
[[ "$output" = *"==START INIT SCRIPT=="* ]]
112+
[[ "$output" = *"hello from inside the container"* ]]
113+
[[ "$output" = *"==END INIT SCRIPT=="* ]]
114+
}

0 commit comments

Comments
 (0)