1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
summary: Each snap app and hook is tracked via cgroups
details: |
This test creates a snap process that suspends itself and ensures that it
placed into the appropriate hierarchy.
systems:
# Ubuntu 14.04's special version of systemd doesn't have StartTransientUnit API.
# This is covered in more depth in the sister cgroup-tracking-failure test.
- -ubuntu-14.04-*
environment:
USER/root: root
USER/test: test
prepare: |
snap pack test-snapd-tracking
tests.cleanup defer rm -f test-snapd-tracking_1_all.snap
snap install --dangerous ./test-snapd-tracking_1_all.snap
tests.cleanup defer snap remove test-snapd-tracking
tests.session -u "$USER" prepare
tests.cleanup defer tests.session -u "$USER" restore
restore: |
rm -f /tmp/*.pid /tmp/*.stamp
if [ "$USER" = root ]; then
systemctl --user stop dbus.service || true
fi
debug: |
cat /proc/self/cgroup
systemctl --version || true
cat /proc/cmdline
execute: |
# This test varies between Ubuntu 16.04, Ubuntu 18.04 and Fedora 31.
# This combination exercises each of the three cases below, namely:
# - pure cgroup v2 system, like Fedora 31
# - hybrid cgroup system, like Ubuntu 18.04
# - pure cgroup v1 system, like Ubuntu 16.04
echo "Find the path and id of the cgroup snapd uses for tracking."
if [ "$(stat -f --print=%T /sys/fs/cgroup)" = "cgroup2fs" ]; then
base_cg_path=/sys/fs/cgroup
base_cg_id=0
elif [ "$(stat -f --print=%T /sys/fs/cgroup/unified)" = "cgroup2fs" ]; then
base_cg_path=/sys/fs/cgroup/unified
base_cg_id=0
elif [ "$(stat -f --print=%T /sys/fs/cgroup/systemd)" = "cgroupfs" ]; then
base_cg_path=/sys/fs/cgroup/systemd
base_cg_id="$(grep -F 'name=systemd' < /proc/self/cgroup | cut -d : -f 1)"
else
echo "cannot find any tracking cgroup"
exit 1
fi
echo "Sanity check, base directory of selected cgroup exists."
test -d "$base_cg_path"
# The configure hook was executed and used a scope for tracking. The scope
# was attached to the system slice, as it is not associated with any user.
test -f /var/snap/test-snapd-tracking/common/configure.cgroup
hook_tracking_cg_path="$(grep -E "^$base_cg_id:" < /var/snap/test-snapd-tracking/common/configure.cgroup | cut -d : -f 3)"
echo "$hook_tracking_cg_path" | MATCH '/system\.slice/snap\.test-snapd-tracking\.hook\.configure-[0-9a-f-]+\.scope'
# The nap service was executed and was tracked as a systemd service.
test -f /var/snap/test-snapd-tracking/common/nap.cgroup
service_tracking_cg_path="$(grep -E "^$base_cg_id:" < /var/snap/test-snapd-tracking/common/nap.cgroup | cut -d : -f 3)"
echo "$service_tracking_cg_path" | MATCH '/system\.slice/snap\.test-snapd-tracking\.nap\.service'
# The application tracking is tested below.
systemd_ver="$(systemctl --version | awk '/systemd [0-9]+/ { print $2 }' | cut -f1 -d"~")"
if [ "$USER" = test ] && "$TESTSTOOLS"/version-compare --strict "$systemd_ver" -lt 238; then
echo "Systemd running as user session manager cannot start transient units"
echo "that cross ownership boundary. This test effectively runs through root"
echo "that is using ssh to connect to a test machine. Crossing the boundary"
echo "of cgroup ownership is allowed by systemd 238 or newer, thanks to"
echo "org.freedesktop.systemd1.Manager.AttachProcessesToUnit method,"
echo "offered by systemd running as system manager, which allows"
echo "overcoming this kernel limitation".
echo "SKIP: systemd is too old to perform this test as user"
# This effectively can run starting with Ubuntu 19.10, Fedora 30,
# Debian 10 (but no version of openSUSE Leap yet) - older versions will
# not perform in this unique environment that spread provides for us.
# In typical desktop environment going all the way back to 16.04 should
# work fine, as far as the intended use-case is concerned.
exit 0
fi
if [ "$USER" = test ]; then
case "$SPREAD_SYSTEM" in
# XXX: this may now pass on core18+, verify that.
ubuntu-core-*)
echo "On core systems there is no session bus so the test variant does not make much sense"
echo "Please see the cgroup-tracking-failure test for more extensive analysis"
echo "SKIP: core systems don't have session services / session bus"
exit 0
;;
esac
fi
echo "Start a \"sleep\" process in the background, in a new session"
# NOTE: tests.session handles PAM and that necessitates a management process
# that eventually performs PAM termination activities. The PID returned from
# invoking tests.session in the background will not be that of the invoked program
# but rather of the monitoring helper process.
tests.session -p /tmp/1.pid -u "$USER" exec snap run test-snapd-tracking.sh -c 'touch /tmp/1.stamp && exec sleep 3m' &
session1_pid=$!
trap "pkill sleep || true" EXIT
echo "Ensure that snap-confine has finished its task and that the snap process"
echo "is active. Note that we don't want to wait forever either."
retry -n 30 test -e /tmp/snap-private-tmp/snap.test-snapd-tracking/tmp/1.stamp
pid1_sleep=$(cat /tmp/1.pid)
echo "During startup snap-run has asked systemd to move the process to a"
echo "new transient scope. The scope name is \"snap.\$random.test-snapd-tracking.sh\"."
echo "Let's verify that."
pid1_tracking_cg_path="$(grep -E "^$base_cg_id:" < "/proc/$pid1_sleep/cgroup" | cut -d : -f 3)"
echo "$pid1_tracking_cg_path" | MATCH '.*/snap\.test-snapd-tracking\.sh-[0-9a-f-]+\.scope'
echo "Sanity check, cgroup associated with the scope exists."
test -d "${base_cg_path}${pid1_tracking_cg_path}"
echo "While the process is alive its PID can be seen in the cgroup.procs file."
MATCH "$pid1_sleep" < "${base_cg_path}${pid1_tracking_cg_path}/cgroup.procs"
echo "Start a second process so that we can check each scope is independent."
#shellcheck disable=SC2016
tests.session -p /tmp/2.pid -u "$USER" exec snap run test-snapd-tracking.sh -c 'touch /tmp/2.stamp && exec sleep 2m' &
session2_pid=$!
retry -n 30 test -e /tmp/snap-private-tmp/snap.test-snapd-tracking/tmp/2.stamp
pid2_sleep=$(cat /tmp/2.pid)
pid2_tracking_cg_path="$(grep -E "^$base_cg_id:" < "/proc/$pid2_sleep/cgroup" | cut -d : -f 3)"
echo "$pid2_tracking_cg_path" | MATCH '.*/snap\.test-snapd-tracking\.sh-[0-9a-f-]+\.scope'
MATCH "$pid2_sleep" < "${base_cg_path}${pid2_tracking_cg_path}/cgroup.procs"
echo "Each invocation uses a new transient scope and thus a new cgroup path."
test "$pid1_tracking_cg_path" != "$pid2_tracking_cg_path"
echo "When the process terminates, the control group it used to exist in is"
echo "automatically removed by systemd, though perhaps not instantly"
kill "$pid1_sleep"
wait "$session1_pid" || true # wait returns the exit code and we kill the process
retry -n 10 test ! -e "${base_cg_path}${pid1_tracking_cg_path}"
kill "$pid2_sleep"
wait "$session2_pid" || true # same as above
retry -n 10 test ! -e "${base_cg_path}${pid2_tracking_cg_path}"
echo "If a snap command forks a child process it is also tracked."
#shellcheck disable=SC2016
tests.session -p /tmp/3.pid -u "$USER" exec snap run test-snapd-tracking.sh -c 'touch /tmp/3.stamp && sleep 1m' &
session3_pid=$!
retry -n 30 test -e /tmp/snap-private-tmp/snap.test-snapd-tracking/tmp/3.stamp
pid3_sh=$(cat /tmp/3.pid)
pid3_tracking_cg_path="$(grep -E "^$base_cg_id:" < "/proc/$pid3_sh/cgroup" | cut -d : -f 3)"
MATCH "$pid3_sh" < "${base_cg_path}${pid3_tracking_cg_path}/cgroup.procs"
echo "Because the script above used \"sleep 1m\" instead of \"exec sleep 1m\" there"
echo "are now two processes: the shell and sleep."
cgroup_procs_path="${base_cg_path}${pid3_tracking_cg_path}/cgroup.procs"
test "$(wc -l < "$cgroup_procs_path")" -eq 2
kill "$pid3_sh"
#shellcheck disable=SC2016
cgroup_procs_path="$cgroup_procs_path" retry -n 10 --wait 0.5 sh -c 'test "$(wc -l < $cgroup_procs_path)" -eq 1'
wait "$session3_pid" || true # same as above
# When tests.session is used with systemd-run
if [ "$systemd_ver" -gt 252 ]; then
pid3_sleep="$(cat "$cgroup_procs_path")"
test -e "${base_cg_path}${pid3_tracking_cg_path}"
kill "$pid3_sleep"
fi
retry -n 10 test ! -e "${base_cg_path}${pid3_tracking_cg_path}"
if [ "$USER" != "root" ]; then
# since the service we run here writes under /var/snap/ we need to make
# that location writable
chmod a+w /var/snap/test-snapd-tracking/common
fi
echo "A service run directly as the user will also get a tracking scope"
rm -f /var/snap/test-snapd-tracking/common/nap.cgroup
tests.session -p /tmp/4.pid -u "$USER" exec snap run test-snapd-tracking.nap &
session4_pid=$!
retry -n 30 test -e /var/snap/test-snapd-tracking/common/nap.cgroup
pid4=$(cat /tmp/4.pid)
pid4_tracking_cg_path="$(grep -E "^$base_cg_id:" < "/proc/$pid4/cgroup" | cut -d : -f 3)"
echo "$pid4_tracking_cg_path" | MATCH '.*/snap\.test-snapd-tracking\.nap-[0-9a-f-]+\.scope'
kill "$pid4"
wait "$session4_pid" || true
|