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