From 762959047c1e1a5d4b6a6fe85cfa8a14c622e4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= Date: Fri, 3 Apr 2020 09:13:51 +0200 Subject: [PATCH] test: add test for cgroup v2 freezer support (cherry picked from commit d446ae89c0168f17eed7135ac06df3b294b3fcc6) Related: #1830861 --- ci/travis-centos-rhel8.sh | 2 +- test/TEST-38-FREEZER/Makefile | 1 + test/TEST-38-FREEZER/test.sh | 53 ++++++ test/TEST-38-FREEZER/testsuite.sh | 293 ++++++++++++++++++++++++++++++ 4 files changed, 348 insertions(+), 1 deletion(-) create mode 120000 test/TEST-38-FREEZER/Makefile create mode 100755 test/TEST-38-FREEZER/test.sh create mode 100755 test/TEST-38-FREEZER/testsuite.sh diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh index a1502e15ee..cd0857fd29 100755 --- a/ci/travis-centos-rhel8.sh +++ b/ci/travis-centos-rhel8.sh @@ -15,7 +15,7 @@ CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" DOCKER_RUN="${DOCKER_RUN:-docker run}" REPO_ROOT="${REPO_ROOT:-$PWD}" -ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq) +ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq diffutils) # RHEL8 options CONFIGURE_OPTS=( -Dsysvinit-path=/etc/rc.d/init.d diff --git a/test/TEST-38-FREEZER/Makefile b/test/TEST-38-FREEZER/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-38-FREEZER/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-38-FREEZER/test.sh b/test/TEST-38-FREEZER/test.sh new file mode 100755 index 0000000000..0d00754b5c --- /dev/null +++ b/test/TEST-38-FREEZER/test.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -e +TEST_DESCRIPTION="test unit freezing and thawing via DBus and systemctl" +TEST_NO_NSPAWN=1 +. $TEST_BASE_DIR/test-functions + +test_setup() { + create_empty_image + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + + ( + LOG_LEVEL=5 + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment + dracut_install tr cut timeout + + # mask some services that we do not want to run in these tests + ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket + ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service + ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service < $initdir/etc/systemd/system/testsuite-38-sleep.service << EOF +[Service] +ExecStart=/bin/sleep 3600 +EOF + + cp testsuite.sh $initdir/ + setup_testsuite + ) + setup_nspawn_root + + ddebug "umount $TESTDIR/root" + umount $TESTDIR/root +} + + +do_test "$@" diff --git a/test/TEST-38-FREEZER/testsuite.sh b/test/TEST-38-FREEZER/testsuite.sh new file mode 100755 index 0000000000..6fcadb8f8e --- /dev/null +++ b/test/TEST-38-FREEZER/testsuite.sh @@ -0,0 +1,293 @@ +#!/usr/bin/env bash + +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +unit=testsuite-38-sleep.service + +start_test_service() { + systemctl daemon-reload + systemctl start "${unit}" +} + +dbus_freeze() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Freeze +} + +dbus_thaw() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Thaw +} + +dbus_freeze_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + FreezeUnit \ + s \ + "$1" +} + +dbus_thaw_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + ThawUnit \ + s \ + "$1" +} + +dbus_can_freeze() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + CanFreeze +} + +check_freezer_state() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + state=$(busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + FreezerState | cut -d " " -f2 | tr -d '"') + + [ "$state" = "$2" ] || { + echo "error: unexpected freezer state, expected: $2, actual: $state" >&2 + exit 1 + } +} + +check_cgroup_state() { + grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events +} + +test_dbus_api() { + echo "Test that DBus API works:" + echo -n " - Freeze(): " + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - Thaw(): " + dbus_thaw "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - FreezeUnit(): " + dbus_freeze_unit "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - ThawUnit(): " + dbus_thaw_unit "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - CanFreeze(): " + output=$(dbus_can_freeze "${unit}") + [ "$output" = "b true" ] + echo "[ OK ]" + + echo +} + +test_jobs() { + local pid_before= + local pid_after= + echo "Test that it is possible to apply jobs on frozen units:" + + systemctl start "${unit}" + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + + echo -n " - restart: " + pid_before=$(systemctl show -p MainPID "${unit}" --value) + systemctl restart "${unit}" + pid_after=$(systemctl show -p MainPID "${unit}" --value) + [ "$pid_before" != "$pid_after" ] && echo "[ OK ]" + + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + + echo -n " - stop: " + timeout 5s systemctl stop "${unit}" + echo "[ OK ]" + + echo +} + +test_systemctl() { + echo "Test that systemctl freeze/thaw verbs:" + + systemctl start "$unit" + + echo -n " - freeze: " + systemctl freeze "$unit" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + # Freezing already frozen unit should be NOP and return quickly + timeout 3s systemctl freeze "$unit" + echo "[ OK ]" + + echo -n " - thaw: " + systemctl thaw "$unit" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + # Likewise thawing already running unit shouldn't block + timeout 3s systemctl thaw "$unit" + echo "[ OK ]" + + systemctl stop "$unit" + + echo +} + +test_systemctl_show() { + echo "Test systemctl show integration:" + + systemctl start "$unit" + + echo -n " - FreezerState property: " + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "running" ] + systemctl freeze "$unit" + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "frozen" ] + systemctl thaw "$unit" + echo "[ OK ]" + + echo -n " - CanFreeze property: " + state=$(systemctl show -p CanFreeze --value "$unit") + [ "$state" = "yes" ] + echo "[ OK ]" + + systemctl stop "$unit" + echo +} + +test_recursive() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test recursive freezing:" + + echo -n " - freeze: " + systemctl freeze "$slice" + check_freezer_state "${slice}" "frozen" + check_freezer_state "${unit}" "frozen" + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw: " + systemctl thaw "$slice" + check_freezer_state "${unit}" "running" + check_freezer_state "${slice}" "running" + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +test_preserve_state() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):" + + echo -n " - freeze from outside: " + echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + + # Our state should not be affected + check_freezer_state "${slice}" "running" + check_freezer_state "${unit}" "running" + + # However actual kernel state should be frozen + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw from outside: " + echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + check_freezer_state "${unit}" "running" + check_freezer_state "${slice}" "running" + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw from outside while inner service is frozen: " + systemctl freeze "$unit" + check_freezer_state "${unit}" "frozen" + echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + check_freezer_state "${slice}" "running" + check_freezer_state "${unit}" "frozen" + echo "[ OK ]" + + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +test -e /sys/fs/cgroup/system.slice/cgroup.freeze && { + start_test_service + test_dbus_api + test_systemctl + test_systemctl_show + test_jobs + test_recursive + test_preserve_state +} + +echo OK > /testok +exit 0