mirror of
https://github.com/kata-containers/cgroups-rs.git
synced 2026-08-05 02:13:23 +00:00
ci: Test cgroup v1 and v2 in QEMU
Run the complete test suite in pinned Ubuntu guests so cgroupfs and systemd coverage does not depend on the GitHub runner's hierarchy. Cross-compile static test binaries on the host to keep TCG execution practical. Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
This commit is contained in:
118
.github/workflows/bvt.yaml
vendored
118
.github/workflows/bvt.yaml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- run: rustup install ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }}
|
||||
- run: make debug
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
name: Format Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- run: rustup install ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }}
|
||||
- run: rustup component add rustfmt
|
||||
- run: make fmt
|
||||
@@ -23,15 +23,117 @@ jobs:
|
||||
name: Clippy Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- run: rustup install ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }}
|
||||
- run: rustup component add clippy
|
||||
- run: make clippy
|
||||
test:
|
||||
name: Run Unit Test
|
||||
runs-on: ubuntu-latest
|
||||
name: Unit and Integration Test / ${{ matrix.hierarchy }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- hierarchy: v1
|
||||
unexpected_skip: Skipping test in cgroups v2 mode
|
||||
- hierarchy: v2
|
||||
unexpected_skip: Skipping test in cgroups v1 mode
|
||||
env:
|
||||
CGROUP_HIERARCHY: ${{ matrix.hierarchy }}
|
||||
CGROUP_IMAGE_NAME: ubuntu-22.04-server-cloudimg-amd64.img
|
||||
CGROUP_IMAGE_SHA256: 757908b2fd6d5b1431bb45070fc1f56cbf017d4025568d292ece37d9cc75e812
|
||||
CGROUP_IMAGE_URL: https://cloud-images.ubuntu.com/releases/jammy/release-20260722/ubuntu-22.04-server-cloudimg-amd64.img
|
||||
UNEXPECTED_SKIP: ${{ matrix.unexpected_skip }}
|
||||
QEMU_ACCEL: tcg
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: rustup install ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }}
|
||||
- run: make test
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Rust
|
||||
run: |
|
||||
rustup toolchain install "${RUST_VERSION}" --profile minimal
|
||||
rustup default "${RUST_VERSION}"
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
|
||||
- name: Install QEMU and build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
binutils \
|
||||
cloud-image-utils \
|
||||
jq \
|
||||
musl-tools \
|
||||
ovmf \
|
||||
qemu-system-x86 \
|
||||
qemu-utils
|
||||
|
||||
- name: Restore pinned Ubuntu cloud image
|
||||
id: image-cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ runner.temp }}/cgroup-image
|
||||
key: ubuntu-jammy-20260722-amd64
|
||||
|
||||
- name: Download Ubuntu cloud image
|
||||
if: steps.image-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p "${RUNNER_TEMP}/cgroup-image"
|
||||
curl --fail --location --retry 3 \
|
||||
--output "${RUNNER_TEMP}/cgroup-image/${CGROUP_IMAGE_NAME}" \
|
||||
"${CGROUP_IMAGE_URL}"
|
||||
|
||||
- name: Verify Ubuntu cloud image
|
||||
run: |
|
||||
echo "${CGROUP_IMAGE_SHA256} ${RUNNER_TEMP}/cgroup-image/${CGROUP_IMAGE_NAME}" \
|
||||
| sha256sum --check -
|
||||
|
||||
- name: Build static test executables
|
||||
run: |
|
||||
ci/qemu/build-static-test-binaries.sh \
|
||||
"${RUNNER_TEMP}/cgroup-test-binaries"
|
||||
|
||||
- name: Create guest SSH key
|
||||
run: |
|
||||
mkdir -p "${RUNNER_TEMP}/cgroup-ssh"
|
||||
ssh-keygen \
|
||||
-q \
|
||||
-t ed25519 \
|
||||
-N "" \
|
||||
-f "${RUNNER_TEMP}/cgroup-ssh/id_ed25519"
|
||||
|
||||
- name: Start cgroup ${{ matrix.hierarchy }} guest
|
||||
run: |
|
||||
ci/qemu/boot-cgroup-test-vm.sh \
|
||||
"${RUNNER_TEMP}/cgroup-vm" \
|
||||
"${RUNNER_TEMP}/cgroup-image/${CGROUP_IMAGE_NAME}" \
|
||||
"${RUNNER_TEMP}/cgroup-ssh/id_ed25519.pub" \
|
||||
"${CGROUP_HIERARCHY}"
|
||||
|
||||
- name: Run cgroupfs and systemd tests
|
||||
run: |
|
||||
set -o pipefail
|
||||
ci/qemu/run-tests-in-cgroup-vm.sh \
|
||||
"${RUNNER_TEMP}/cgroup-vm" \
|
||||
"${RUNNER_TEMP}/cgroup-test-binaries" \
|
||||
"${RUNNER_TEMP}/cgroup-ssh/id_ed25519" \
|
||||
"${CGROUP_HIERARCHY}" \
|
||||
2>&1 | tee "${RUNNER_TEMP}/cgroup-tests.log"
|
||||
if grep -q "${UNEXPECTED_SKIP}" \
|
||||
"${RUNNER_TEMP}/cgroup-tests.log"; then
|
||||
echo "A ${CGROUP_HIERARCHY} test unexpectedly skipped" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Upload QEMU diagnostics
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: cgroup-${{ matrix.hierarchy }}-qemu-diagnostics
|
||||
path: |
|
||||
${{ runner.temp }}/cgroup-vm/console.log
|
||||
${{ runner.temp }}/cgroup-vm/qemu.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Stop cgroup guest
|
||||
if: always()
|
||||
run: ci/qemu/stop-cgroup-test-vm.sh "${RUNNER_TEMP}/cgroup-vm"
|
||||
|
||||
38
ci/qemu/README.md
Normal file
38
ci/qemu/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Reproducible cgroup QEMU test environments
|
||||
|
||||
These scripts boot the same pinned Ubuntu 22.04 guest in explicit cgroup v1
|
||||
and cgroup v2 modes. Both the cgroupfs and systemd backends are therefore
|
||||
tested against real kernel interfaces without inheriting the GitHub runner's
|
||||
cgroup hierarchy, systemd version, mount layout, or delegation policy.
|
||||
|
||||
The workflow intentionally defaults to QEMU TCG software emulation. GitHub
|
||||
documents Android SDK hardware acceleration on Linux runners, but it does not
|
||||
promise general-purpose nested virtualization or `/dev/kvm` access.
|
||||
|
||||
To keep TCG practical, Rust test executables are cross-compiled as static musl
|
||||
binaries on the GitHub runner. Only the completed executables run inside the
|
||||
guest. `run-tests-in-cgroup-vm.sh` transfers them together with
|
||||
`run-test-binaries-in-guest.sh`, which contains the guest-side test ordering
|
||||
and failure cleanup.
|
||||
|
||||
The scripts are named for their responsibilities:
|
||||
|
||||
| Script | Responsibility |
|
||||
| --- | --- |
|
||||
| `build-static-test-binaries.sh` | Cross-compile portable Rust test harnesses |
|
||||
| `boot-cgroup-test-vm.sh` | Configure, boot, and verify a v1 or v2 QEMU guest |
|
||||
| `run-tests-in-cgroup-vm.sh` | Transfer tests and coordinate execution over SSH |
|
||||
| `run-test-binaries-in-guest.sh` | Execute test harnesses inside the guest |
|
||||
| `stop-cgroup-test-vm.sh` | Stop the disposable QEMU guest |
|
||||
|
||||
The test matrix runs entirely in QEMU:
|
||||
|
||||
| Guest mode | Filesystem backend | systemd backend |
|
||||
| --- | --- | --- |
|
||||
| Ubuntu 22.04, cgroup v1 | cgroup v1 | cgroup v1 |
|
||||
| Ubuntu 22.04, cgroup v2 | cgroup v2 | cgroup v2 |
|
||||
|
||||
Both modes are selected with an explicit
|
||||
`systemd.unified_cgroup_hierarchy=<0|1>` kernel argument. Each guest must pass
|
||||
an exact hierarchy check before tests start. This is important because several
|
||||
existing tests return early when their expected cgroup version is unavailable.
|
||||
201
ci/qemu/boot-cgroup-test-vm.sh
Executable file
201
ci/qemu/boot-cgroup-test-vm.sh
Executable file
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Start an Ubuntu 22.04 VM in an explicit cgroup mode. TCG is the default
|
||||
# accelerator because standard GitHub-hosted runners do not promise
|
||||
# general-purpose nested virtualization. Set QEMU_ACCEL=kvm explicitly when
|
||||
# running on a host where /dev/kvm is part of the supported environment.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly USAGE="usage: boot-cgroup-test-vm.sh STATE_DIR BASE_IMAGE PUBLIC_KEY HIERARCHY"
|
||||
readonly STATE_DIR="${1:?${USAGE}}"
|
||||
readonly BASE_IMAGE="${2:?${USAGE}}"
|
||||
readonly PUBLIC_KEY="${3:?${USAGE}}"
|
||||
readonly PRIVATE_KEY="${PUBLIC_KEY%.pub}"
|
||||
readonly HIERARCHY="${4:?${USAGE}}"
|
||||
readonly SSH_PORT="${CGROUP_VM_SSH_PORT:-2222}"
|
||||
readonly QEMU_ACCEL="${QEMU_ACCEL:-tcg}"
|
||||
readonly SSH_USER="runner"
|
||||
|
||||
case "${HIERARCHY}" in
|
||||
v1)
|
||||
unified="0"
|
||||
hierarchy_check='
|
||||
grep -qw systemd.unified_cgroup_hierarchy=0 /proc/cmdline &&
|
||||
test "$(stat -fc %T /sys/fs/cgroup)" != cgroup2fs &&
|
||||
test -n "$(findmnt -rn -t cgroup -o TARGET)"
|
||||
'
|
||||
;;
|
||||
v2)
|
||||
unified="1"
|
||||
hierarchy_check='
|
||||
grep -qw systemd.unified_cgroup_hierarchy=1 /proc/cmdline &&
|
||||
test "$(stat -fc %T /sys/fs/cgroup)" = cgroup2fs &&
|
||||
test -f /sys/fs/cgroup/cgroup.controllers
|
||||
'
|
||||
;;
|
||||
*)
|
||||
echo "unsupported cgroup hierarchy: ${HIERARCHY}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
for command in cloud-localds qemu-img qemu-system-x86_64 ssh; do
|
||||
if ! command -v "${command}" >/dev/null; then
|
||||
echo "required command is missing: ${command}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -e "${STATE_DIR}" ]]; then
|
||||
echo "state directory already exists: ${STATE_DIR}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${BASE_IMAGE}" ]]; then
|
||||
echo "base image does not exist: ${BASE_IMAGE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${PUBLIC_KEY}" ]]; then
|
||||
echo "SSH public key does not exist: ${PUBLIC_KEY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${PRIVATE_KEY}" ]]; then
|
||||
echo "SSH private key does not exist: ${PRIVATE_KEY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${STATE_DIR}"
|
||||
|
||||
readonly DISK="${STATE_DIR}/root.qcow2"
|
||||
readonly SEED="${STATE_DIR}/seed.qcow2"
|
||||
readonly USER_DATA="${STATE_DIR}/user-data"
|
||||
readonly META_DATA="${STATE_DIR}/meta-data"
|
||||
readonly CONSOLE_LOG="${STATE_DIR}/console.log"
|
||||
readonly QEMU_LOG="${STATE_DIR}/qemu.log"
|
||||
readonly PID_FILE="${STATE_DIR}/qemu.pid"
|
||||
|
||||
qemu-img create \
|
||||
-q \
|
||||
-f qcow2 \
|
||||
-F qcow2 \
|
||||
-b "$(realpath "${BASE_IMAGE}")" \
|
||||
"${DISK}" \
|
||||
8G
|
||||
|
||||
ssh_key="$(<"${PUBLIC_KEY}")"
|
||||
|
||||
{
|
||||
echo "#cloud-config"
|
||||
echo "users:"
|
||||
echo " - default"
|
||||
echo " - name: ${SSH_USER}"
|
||||
echo " groups: [adm, sudo]"
|
||||
echo " shell: /bin/bash"
|
||||
echo " sudo: ALL=(ALL) NOPASSWD:ALL"
|
||||
echo " ssh_authorized_keys:"
|
||||
printf ' - %s\n' "${ssh_key}"
|
||||
echo "ssh_pwauth: false"
|
||||
echo "disable_root: true"
|
||||
echo "write_files:"
|
||||
echo " - path: /etc/default/grub.d/99-cgroup-hierarchy.cfg"
|
||||
echo " owner: root:root"
|
||||
echo " permissions: '0644'"
|
||||
echo " content: |"
|
||||
echo " GRUB_CMDLINE_LINUX=\"\${GRUB_CMDLINE_LINUX} systemd.unified_cgroup_hierarchy=${unified}\""
|
||||
echo "runcmd:"
|
||||
echo " - [update-grub]"
|
||||
echo " - [touch, /var/lib/cgroup-hierarchy-configured]"
|
||||
echo "power_state:"
|
||||
echo " mode: reboot"
|
||||
echo " delay: now"
|
||||
echo " timeout: 120"
|
||||
echo " condition: test -f /var/lib/cgroup-hierarchy-configured"
|
||||
} >"${USER_DATA}"
|
||||
|
||||
{
|
||||
echo "instance-id: cgroups-rs-${HIERARCHY}"
|
||||
echo "local-hostname: cgroups-rs-${HIERARCHY}"
|
||||
} >"${META_DATA}"
|
||||
|
||||
cloud-localds --disk-format qcow2 "${SEED}" "${USER_DATA}" "${META_DATA}"
|
||||
|
||||
case "${QEMU_ACCEL}" in
|
||||
tcg)
|
||||
accel_args=(-accel "tcg,thread=multi" -cpu max)
|
||||
;;
|
||||
kvm)
|
||||
if [[ ! -r /dev/kvm || ! -w /dev/kvm ]]; then
|
||||
echo "QEMU_ACCEL=kvm requested but /dev/kvm is not accessible" >&2
|
||||
exit 1
|
||||
fi
|
||||
accel_args=(-accel kvm -cpu host)
|
||||
;;
|
||||
*)
|
||||
echo "unsupported QEMU_ACCEL value: ${QEMU_ACCEL}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
firmware_args=()
|
||||
if [[ -f /usr/share/OVMF/OVMF_CODE.fd && -f /usr/share/OVMF/OVMF_VARS.fd ]]; then
|
||||
cp /usr/share/OVMF/OVMF_VARS.fd "${STATE_DIR}/OVMF_VARS.fd"
|
||||
firmware_args=(
|
||||
-drive "if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd"
|
||||
-drive "if=pflash,format=raw,file=${STATE_DIR}/OVMF_VARS.fd"
|
||||
)
|
||||
fi
|
||||
|
||||
echo "Starting cgroup ${HIERARCHY} guest with QEMU accelerator: ${QEMU_ACCEL}"
|
||||
|
||||
qemu-system-x86_64 \
|
||||
-name "cgroups-rs-${HIERARCHY}" \
|
||||
-machine q35 \
|
||||
"${accel_args[@]}" \
|
||||
-smp 2 \
|
||||
-m 4096 \
|
||||
"${firmware_args[@]}" \
|
||||
-drive "file=${DISK},if=virtio,format=qcow2,cache=writeback" \
|
||||
-drive "file=${SEED},if=virtio,format=qcow2,readonly=on" \
|
||||
-device virtio-rng-pci \
|
||||
-device virtio-net-pci,netdev=net0 \
|
||||
-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:${SSH_PORT}-:22" \
|
||||
-display none \
|
||||
-monitor none \
|
||||
-serial "file:${CONSOLE_LOG}" \
|
||||
-D "${QEMU_LOG}" \
|
||||
-pidfile "${PID_FILE}" \
|
||||
-daemonize
|
||||
|
||||
ssh_options=(
|
||||
-i "${PRIVATE_KEY}"
|
||||
-p "${SSH_PORT}"
|
||||
-o BatchMode=yes
|
||||
-o ConnectTimeout=5
|
||||
-o StrictHostKeyChecking=no
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
)
|
||||
|
||||
# The first boot applies the requested hierarchy kernel argument and reboots.
|
||||
# Waiting for the exact filesystem type prevents that first boot from being
|
||||
# mistaken for the requested test environment.
|
||||
deadline=$((SECONDS + 1200))
|
||||
while ((SECONDS < deadline)); do
|
||||
if ! kill -0 "$(<"${PID_FILE}")" 2>/dev/null; then
|
||||
echo "QEMU exited before the guest became ready" >&2
|
||||
tail -n 200 "${CONSOLE_LOG}" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ssh "${ssh_options[@]}" "${SSH_USER}@127.0.0.1" \
|
||||
"${hierarchy_check}" \
|
||||
>/dev/null 2>&1; then
|
||||
echo "The cgroup ${HIERARCHY} guest is ready"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "timed out waiting for the cgroup ${HIERARCHY} guest" >&2
|
||||
tail -n 200 "${CONSOLE_LOG}" >&2 || true
|
||||
exit 1
|
||||
59
ci/qemu/build-static-test-binaries.sh
Executable file
59
ci/qemu/build-static-test-binaries.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build portable test executables on the GitHub runner. The executables are
|
||||
# copied into the cgroup VM and run there, avoiding a Rust build under QEMU
|
||||
# software emulation.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly TARGET="x86_64-unknown-linux-musl"
|
||||
readonly OUTPUT_DIR="${1:?usage: build-static-test-binaries.sh OUTPUT_DIR}"
|
||||
|
||||
if [[ -e "${OUTPUT_DIR}" ]]; then
|
||||
echo "output directory already exists: ${OUTPUT_DIR}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
|
||||
messages="$(mktemp)"
|
||||
executables="$(mktemp)"
|
||||
manifest="$(mktemp)"
|
||||
trap 'rm -f "${messages}" "${executables}" "${manifest}"' EXIT
|
||||
|
||||
cargo test \
|
||||
--all-features \
|
||||
--target "${TARGET}" \
|
||||
--no-run \
|
||||
--message-format=json >"${messages}"
|
||||
|
||||
jq -r '
|
||||
select(
|
||||
.reason == "compiler-artifact"
|
||||
and .profile.test == true
|
||||
and .executable != null
|
||||
)
|
||||
| .executable
|
||||
' "${messages}" | sort -u >"${executables}"
|
||||
|
||||
if [[ ! -s "${executables}" ]]; then
|
||||
echo "cargo did not produce any test executables" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while IFS= read -r executable; do
|
||||
destination="${OUTPUT_DIR}/$(basename "${executable}")"
|
||||
cp "${executable}" "${destination}"
|
||||
strip "${destination}"
|
||||
|
||||
if readelf -l "${destination}" | grep -q "Requesting program interpreter"; then
|
||||
echo "test executable is dynamically linked: ${destination}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done <"${executables}"
|
||||
|
||||
find "${OUTPUT_DIR}" -maxdepth 1 -type f -printf '%f\n' | sort >"${manifest}"
|
||||
mv "${manifest}" "${OUTPUT_DIR}/manifest"
|
||||
|
||||
echo "Built $(wc -l <"${OUTPUT_DIR}/manifest") static test executables:"
|
||||
sed 's/^/ /' "${OUTPUT_DIR}/manifest"
|
||||
54
ci/qemu/run-test-binaries-in-guest.sh
Executable file
54
ci/qemu/run-test-binaries-in-guest.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run the compiled Rust test harnesses inside a cgroup test VM.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly TEST_DIR="${1:?usage: run-test-binaries-in-guest.sh TEST_DIR}"
|
||||
|
||||
cd "${TEST_DIR}"
|
||||
|
||||
cleanup_failed_test() {
|
||||
# A failed test can leave a spawned helper holding the SSH channel open.
|
||||
# The VM is disposable, so terminate only the helper commands used by
|
||||
# this suite before returning.
|
||||
sudo pkill -KILL -x sleep || true
|
||||
sudo pkill -KILL -x yes || true
|
||||
}
|
||||
|
||||
run_test() {
|
||||
local test_binary="$1"
|
||||
shift
|
||||
|
||||
echo
|
||||
echo "Running ${test_binary} $*"
|
||||
|
||||
local result=0
|
||||
sudo -E "./${test_binary}" "$@" --color always --nocapture \
|
||||
--test-threads=1 || result=$?
|
||||
if ((result != 0)); then
|
||||
cleanup_failed_test
|
||||
exit "${result}"
|
||||
fi
|
||||
}
|
||||
|
||||
library_test="$(grep -E "^cgroups_rs-" manifest)"
|
||||
if [[ "$(wc -l <<<"${library_test}")" -ne 1 ]]; then
|
||||
echo "expected exactly one library test executable" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Match the phases in the Makefile: cgroup-manipulating suites are isolated
|
||||
# and sequential, followed by all remaining unit tests.
|
||||
run_test "${library_test}" systemd::dbus::client::tests
|
||||
run_test "${library_test}" manager::fs::tests
|
||||
run_test "${library_test}" manager::systemd::tests
|
||||
run_test "${library_test}" \
|
||||
--skip systemd::dbus::client::tests \
|
||||
--skip manager::fs::tests \
|
||||
--skip manager::systemd::tests
|
||||
|
||||
while IFS= read -r test_binary; do
|
||||
[[ "${test_binary}" == "${library_test}" ]] && continue
|
||||
run_test "${test_binary}"
|
||||
done < manifest
|
||||
86
ci/qemu/run-tests-in-cgroup-vm.sh
Executable file
86
ci/qemu/run-tests-in-cgroup-vm.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copy statically linked test executables into a cgroup VM and run them
|
||||
# sequentially. Running every executable covers library and integration tests.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly USAGE="usage: run-tests-in-cgroup-vm.sh STATE_DIR TEST_BINARIES PRIVATE_KEY HIERARCHY"
|
||||
readonly STATE_DIR="${1:?${USAGE}}"
|
||||
readonly TEST_BINARIES="${2:?${USAGE}}"
|
||||
readonly SSH_PORT="${CGROUP_VM_SSH_PORT:-2222}"
|
||||
readonly SSH_USER="runner"
|
||||
readonly PRIVATE_KEY="${3:?${USAGE}}"
|
||||
readonly HIERARCHY="${4:?${USAGE}}"
|
||||
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
readonly GUEST_TEST_RUNNER="${SCRIPT_DIR}/run-test-binaries-in-guest.sh"
|
||||
|
||||
case "${HIERARCHY}" in
|
||||
v1)
|
||||
hierarchy_check='
|
||||
grep -qw systemd.unified_cgroup_hierarchy=0 /proc/cmdline &&
|
||||
test "$(stat -fc %T /sys/fs/cgroup)" != cgroup2fs &&
|
||||
test -n "$(findmnt -rn -t cgroup -o TARGET)"
|
||||
'
|
||||
;;
|
||||
v2)
|
||||
hierarchy_check='
|
||||
grep -qw systemd.unified_cgroup_hierarchy=1 /proc/cmdline &&
|
||||
test "$(stat -fc %T /sys/fs/cgroup)" = cgroup2fs &&
|
||||
test -f /sys/fs/cgroup/cgroup.controllers
|
||||
'
|
||||
;;
|
||||
*)
|
||||
echo "unsupported cgroup hierarchy: ${HIERARCHY}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ ! -f "${STATE_DIR}/qemu.pid" ]]; then
|
||||
echo "QEMU pid file is missing from ${STATE_DIR}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${TEST_BINARIES}/manifest" ]]; then
|
||||
echo "test manifest is missing from ${TEST_BINARIES}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${PRIVATE_KEY}" ]]; then
|
||||
echo "SSH private key is missing: ${PRIVATE_KEY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "${GUEST_TEST_RUNNER}" ]]; then
|
||||
echo "guest test runner is missing: ${GUEST_TEST_RUNNER}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ssh_options=(
|
||||
-i "${PRIVATE_KEY}"
|
||||
-p "${SSH_PORT}"
|
||||
-o BatchMode=yes
|
||||
-o ConnectTimeout=10
|
||||
-o StrictHostKeyChecking=no
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
)
|
||||
|
||||
echo "Guest hierarchy:"
|
||||
ssh "${ssh_options[@]}" "${SSH_USER}@127.0.0.1" \
|
||||
'set -e;
|
||||
systemd --version | head -n 1;
|
||||
findmnt -rn -t cgroup,cgroup2 -o FSTYPE,TARGET,OPTIONS;
|
||||
echo;
|
||||
cat /proc/cgroups'
|
||||
|
||||
ssh "${ssh_options[@]}" "${SSH_USER}@127.0.0.1" \
|
||||
"${hierarchy_check}"
|
||||
|
||||
ssh "${ssh_options[@]}" "${SSH_USER}@127.0.0.1" \
|
||||
'mkdir -p /tmp/cgroups-rs-tests'
|
||||
|
||||
tar -czf - \
|
||||
-C "${TEST_BINARIES}" . \
|
||||
-C "${SCRIPT_DIR}" "$(basename "${GUEST_TEST_RUNNER}")" \
|
||||
| ssh "${ssh_options[@]}" "${SSH_USER}@127.0.0.1" \
|
||||
'tar -xzf - -C /tmp/cgroups-rs-tests'
|
||||
|
||||
ssh "${ssh_options[@]}" "${SSH_USER}@127.0.0.1" \
|
||||
'bash /tmp/cgroups-rs-tests/run-test-binaries-in-guest.sh /tmp/cgroups-rs-tests'
|
||||
53
ci/qemu/stop-cgroup-test-vm.sh
Executable file
53
ci/qemu/stop-cgroup-test-vm.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Stop a cgroup test VM started by boot-cgroup-test-vm.sh.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly STATE_DIR="${1:?usage: stop-cgroup-test-vm.sh STATE_DIR}"
|
||||
readonly PID_FILE="${STATE_DIR}/qemu.pid"
|
||||
readonly DISK="${STATE_DIR}/root.qcow2"
|
||||
|
||||
if [[ ! -f "${PID_FILE}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pid="$(<"${PID_FILE}")"
|
||||
if ! kill -0 "${pid}" 2>/dev/null; then
|
||||
rm -f -- "${PID_FILE}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -r "/proc/${pid}/cmdline" ]]; then
|
||||
echo "cannot verify process ${pid} from ${PID_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cmdline="$(tr '\0' '\n' <"/proc/${pid}/cmdline")"
|
||||
if [[ "${cmdline}" != *"${DISK}"* ]]; then
|
||||
echo "refusing to stop unrelated process ${pid} from stale ${PID_FILE}" >&2
|
||||
rm -f -- "${PID_FILE}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
kill "${pid}"
|
||||
|
||||
for _ in {1..20}; do
|
||||
if ! kill -0 "${pid}" 2>/dev/null; then
|
||||
rm -f -- "${PID_FILE}"
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
kill -KILL "${pid}" 2>/dev/null || true
|
||||
for _ in {1..20}; do
|
||||
if ! kill -0 "${pid}" 2>/dev/null; then
|
||||
rm -f -- "${PID_FILE}"
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "QEMU process ${pid} did not stop" >&2
|
||||
exit 1
|
||||
Reference in New Issue
Block a user