...
1#!/usr/bin/env bats
2
3load helpers
4
5function teardown() {
6 teardown_bundle
7}
8
9function setup() {
10 setup_busybox
11}
12
13@test "runc create (no limits + no cgrouppath + no permission) succeeds" {
14 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_permissions
15 [ "$status" -eq 0 ]
16}
17
18@test "runc create (rootless + no limits + cgrouppath + no permission) fails with permission error" {
19 requires rootless rootless_no_cgroup
20
21 set_cgroups_path
22
23 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_permissions
24 [ "$status" -eq 1 ]
25 [[ "$output" == *"unable to apply cgroup configuration"*"permission denied"* ]]
26}
27
28@test "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error" {
29 requires rootless rootless_no_cgroup
30
31 set_resources_limit
32
33 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_permissions
34 [ "$status" -eq 1 ]
35 [[ "$output" == *"rootless needs no limits + no cgrouppath when no permission is granted for cgroups"* ]] ||
36 [[ "$output" == *"cannot set pids limit: container could not join or create cgroup"* ]]
37}
38
39@test "runc create (limits + cgrouppath + permission on the cgroup dir) succeeds" {
40 [[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
41
42 set_cgroups_path
43 set_resources_limit
44
45 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_permissions
46 [ "$status" -eq 0 ]
47 if [ "$CGROUP_UNIFIED" != "no" ]; then
48 if [ -n "${RUNC_USE_SYSTEMD}" ]; then
49 if [ "$(id -u)" = "0" ]; then
50 check_cgroup_value "cgroup.controllers" "$(cat /sys/fs/cgroup/machine.slice/cgroup.controllers)"
51 else
52 # Filter out controllers that systemd is unable to delegate.
53 check_cgroup_value "cgroup.controllers" "$(sed 's/ \(hugetlb\|misc\|rdma\)//g' </sys/fs/cgroup/user.slice/user-"$(id -u)".slice/cgroup.controllers)"
54 fi
55 else
56 check_cgroup_value "cgroup.controllers" "$(cat /sys/fs/cgroup/cgroup.controllers)"
57 fi
58 fi
59}
60
61@test "runc exec (limits + cgrouppath + permission on the cgroup dir) succeeds" {
62 [[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
63
64 set_cgroups_path
65 set_resources_limit
66
67 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_permissions
68 [ "$status" -eq 0 ]
69
70 runc exec test_cgroups_permissions echo "cgroups_exec"
71 [ "$status" -eq 0 ]
72 [[ ${lines[0]} == *"cgroups_exec"* ]]
73}
74
75@test "runc exec (cgroup v2 + init process in non-root cgroup) succeeds" {
76 requires root cgroups_v2
77
78 set_cgroups_path
79 set_cgroup_mount_writable
80
81 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_group
82 [ "$status" -eq 0 ]
83
84 runc exec test_cgroups_group cat /sys/fs/cgroup/cgroup.controllers
85 [ "$status" -eq 0 ]
86 [[ ${lines[0]} == *"memory"* ]]
87
88 runc exec test_cgroups_group cat /proc/self/cgroup
89 [ "$status" -eq 0 ]
90 [[ ${lines[0]} == "0::/" ]]
91
92 runc exec test_cgroups_group mkdir /sys/fs/cgroup/foo
93 [ "$status" -eq 0 ]
94
95 runc exec test_cgroups_group sh -c "echo 1 > /sys/fs/cgroup/foo/cgroup.procs"
96 [ "$status" -eq 0 ]
97
98 # the init process is now in "/foo", but an exec process can still join "/"
99 # because we haven't enabled any domain controller.
100 runc exec test_cgroups_group cat /proc/self/cgroup
101 [ "$status" -eq 0 ]
102 [[ ${lines[0]} == "0::/" ]]
103
104 # turn on a domain controller (memory)
105 runc exec test_cgroups_group sh -euxc 'echo $$ > /sys/fs/cgroup/foo/cgroup.procs; echo +memory > /sys/fs/cgroup/cgroup.subtree_control'
106 [ "$status" -eq 0 ]
107
108 # an exec process can no longer join "/" after turning on a domain controller.
109 # falls back to "/foo".
110 runc exec test_cgroups_group cat /proc/self/cgroup
111 [ "$status" -eq 0 ]
112 [[ ${lines[0]} == "0::/foo" ]]
113
114 # teardown: remove "/foo"
115 # shellcheck disable=SC2016
116 runc exec test_cgroups_group sh -uxc 'echo -memory > /sys/fs/cgroup/cgroup.subtree_control; for f in $(cat /sys/fs/cgroup/foo/cgroup.procs); do echo $f > /sys/fs/cgroup/cgroup.procs; done; rmdir /sys/fs/cgroup/foo'
117 runc exec test_cgroups_group test ! -d /sys/fs/cgroup/foo
118 [ "$status" -eq 0 ]
119 #
120}
121
122@test "runc run (cgroup v1 + unified resources should fail)" {
123 requires root cgroups_v1
124
125 set_cgroups_path
126 set_resources_limit
127 update_config '.linux.resources.unified |= {"memory.min": "131072"}'
128
129 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified
130 [ "$status" -ne 0 ]
131 [[ "$output" == *'invalid configuration'* ]]
132}
133
134@test "runc run (blkio weight)" {
135 requires cgroups_v2
136 [[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
137
138 set_cgroups_path
139 update_config '.linux.resources.blockIO |= {"weight": 750}'
140
141 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified
142 [ "$status" -eq 0 ]
143
144 runc exec test_cgroups_unified sh -c 'cat /sys/fs/cgroup/io.bfq.weight'
145 if [[ "$status" -eq 0 ]]; then
146 [ "$output" = 'default 750' ]
147 else
148 runc exec test_cgroups_unified sh -c 'cat /sys/fs/cgroup/io.weight'
149 [ "$output" = 'default 7475' ]
150 fi
151}
152
153@test "runc run (per-device io weight for bfq)" {
154 requires root # to create a loop device
155
156 dd if=/dev/zero of=backing.img bs=4096 count=1
157 dev=$(losetup --find --show backing.img) || skip "unable to create a loop device"
158
159 # See if BFQ scheduler is available.
160 if ! { grep -qw bfq "/sys/block/${dev#/dev/}/queue/scheduler" &&
161 echo bfq >"/sys/block/${dev#/dev/}/queue/scheduler"; }; then
162 losetup -d "$dev"
163 skip "BFQ scheduler not available"
164 fi
165
166 set_cgroups_path
167
168 IFS=$' \t:' read -r major minor <<<"$(lsblk -nd -o MAJ:MIN "$dev")"
169 update_config ' .linux.devices += [{path: "'"$dev"'", type: "b", major: '"$major"', minor: '"$minor"'}]
170 | .linux.resources.blockIO.weight |= 333
171 | .linux.resources.blockIO.weightDevice |= [
172 { major: '"$major"', minor: '"$minor"', weight: 444 }
173 ]'
174 runc run -d --console-socket "$CONSOLE_SOCKET" test_dev_weight
175 [ "$status" -eq 0 ]
176
177 # The loop device itself is no longer needed.
178 losetup -d "$dev"
179
180 if [ "$CGROUP_UNIFIED" = "yes" ]; then
181 file="io.bfq.weight"
182 else
183 file="blkio.bfq.weight_device"
184 fi
185 weights=$(get_cgroup_value $file)
186 [[ "$weights" == *"default 333"* ]]
187 [[ "$weights" == *"$major:$minor 444"* ]]
188}
189
190# Convert size in KB to hugetlb size suffix.
191convert_hugetlb_size() {
192 local size=$1
193 local units=("KB" "MB" "GB")
194 local idx=0
195
196 while ((size >= 1024)); do
197 ((size /= 1024))
198 ((idx++))
199 done
200
201 echo "$size${units[$idx]}"
202}
203
204@test "runc run (hugetlb limits)" {
205 requires cgroups_hugetlb
206 [ $EUID -ne 0 ] && requires rootless_cgroup
207 # shellcheck disable=SC2012 # ls is fine here.
208 mapfile -t sizes_kb < <(ls /sys/kernel/mm/hugepages/ | sed -e 's/.*hugepages-//' -e 's/kB$//') #
209 if [ "${#sizes_kb[@]}" -lt 1 ]; then
210 skip "requires hugetlb"
211 fi
212
213 # Create two arrays:
214 # - sizes: hugetlb cgroup file suffixes;
215 # - limits: limits for each size.
216 for size in "${sizes_kb[@]}"; do
217 sizes+=("$(convert_hugetlb_size "$size")")
218 # Limit to 1 page.
219 limits+=("$((size * 1024))")
220 done
221
222 # Set per-size limits.
223 for ((i = 0; i < ${#sizes[@]}; i++)); do
224 size="${sizes[$i]}"
225 limit="${limits[$i]}"
226 update_config '.linux.resources.hugepageLimits += [{ pagesize: "'"$size"'", limit: '"$limit"' }]'
227 done
228
229 set_cgroups_path
230 runc run -d --console-socket "$CONSOLE_SOCKET" test_hugetlb
231 [ "$status" -eq 0 ]
232
233 lim="max"
234 [ "$CGROUP_UNIFIED" = "no" ] && lim="limit_in_bytes"
235
236 optional=("")
237 # Add rsvd, if available.
238 if test -f "$(get_cgroup_path hugetlb)/hugetlb.${sizes[0]}.rsvd.$lim"; then
239 optional+=(".rsvd")
240 fi
241
242 # Check if the limits are as expected.
243 for ((i = 0; i < ${#sizes[@]}; i++)); do
244 size="${sizes[$i]}"
245 limit="${limits[$i]}"
246 for rsvd in "${optional[@]}"; do
247 param="hugetlb.${size}${rsvd}.$lim"
248 echo "checking $param"
249 check_cgroup_value "$param" "$limit"
250 done
251 done
252}
253
254@test "runc run (cgroup v2 resources.unified only)" {
255 requires root cgroups_v2
256
257 set_cgroups_path
258 update_config ' .linux.resources.unified |= {
259 "memory.min": "131072",
260 "memory.low": "524288",
261 "memory.high": "5242880",
262 "memory.max": "10485760",
263 "memory.swap.max": "20971520",
264 "pids.max": "99",
265 "cpu.max": "10000 100000",
266 "cpu.weight": "42"
267 }'
268
269 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified
270 [ "$status" -eq 0 ]
271
272 runc exec test_cgroups_unified sh -c 'cd /sys/fs/cgroup && grep . *.min *.max *.low *.high'
273 [ "$status" -eq 0 ]
274 echo "$output"
275
276 echo "$output" | grep -q '^memory.min:131072$'
277 echo "$output" | grep -q '^memory.low:524288$'
278 echo "$output" | grep -q '^memory.high:5242880$'
279 echo "$output" | grep -q '^memory.max:10485760$'
280 echo "$output" | grep -q '^memory.swap.max:20971520$'
281 echo "$output" | grep -q '^pids.max:99$'
282 echo "$output" | grep -q '^cpu.max:10000 100000$'
283
284 check_systemd_value "MemoryMin" 131072
285 check_systemd_value "MemoryLow" 524288
286 check_systemd_value "MemoryHigh" 5242880
287 check_systemd_value "MemoryMax" 10485760
288 check_systemd_value "MemorySwapMax" 20971520
289 check_systemd_value "TasksMax" 99
290 check_cpu_quota 10000 100000 "100ms"
291 check_cpu_weight 42
292}
293
294@test "runc run (cgroup v2 resources.unified override)" {
295 requires root cgroups_v2
296
297 set_cgroups_path
298 # CPU shares of 3333 corresponds to CPU weight of 128.
299 update_config ' .linux.resources.memory |= {"limit": 33554432}
300 | .linux.resources.cpu |= {
301 "shares": 3333,
302 "quota": 40000,
303 "period": 100000
304 }
305 | .linux.resources.unified |= {
306 "memory.min": "131072",
307 "memory.max": "10485760",
308 "pids.max": "42",
309 "cpu.max": "5000 50000",
310 "cpu.weight": "42"
311 }'
312
313 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified
314 [ "$status" -eq 0 ]
315
316 runc exec test_cgroups_unified cat /sys/fs/cgroup/memory.min
317 [ "$status" -eq 0 ]
318 [ "$output" = '131072' ]
319
320 runc exec test_cgroups_unified cat /sys/fs/cgroup/memory.max
321 [ "$status" -eq 0 ]
322 [ "$output" = '10485760' ]
323
324 runc exec test_cgroups_unified cat /sys/fs/cgroup/pids.max
325 [ "$status" -eq 0 ]
326 [ "$output" = '42' ]
327 check_systemd_value "TasksMax" 42
328
329 check_cpu_quota 5000 50000 "100ms"
330
331 check_cpu_weight 42
332}
333
334@test "runc run (cgroupv2 mount inside container)" {
335 requires cgroups_v2
336 [[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
337
338 set_cgroups_path
339
340 runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified
341 [ "$status" -eq 0 ]
342
343 # Make sure we don't have any extra cgroups inside
344 runc exec test_cgroups_unified find /sys/fs/cgroup/ -type d
345 [ "$status" -eq 0 ]
346 [ "$(wc -l <<<"$output")" -eq 1 ]
347}
348
349@test "runc exec (cgroup v1+hybrid joins correct cgroup)" {
350 requires root cgroups_hybrid
351
352 set_cgroups_path
353
354 runc run --pid-file pid.txt -d --console-socket "$CONSOLE_SOCKET" test_cgroups_group
355 [ "$status" -eq 0 ]
356
357 pid=$(cat pid.txt)
358 run_cgroup=$(tail -1 </proc/"$pid"/cgroup)
359 [[ "$run_cgroup" == *"runc-cgroups-integration-test"* ]]
360
361 runc exec test_cgroups_group cat /proc/self/cgroup
362 [ "$status" -eq 0 ]
363 exec_cgroup=${lines[-1]}
364 [[ $exec_cgroup == *"runc-cgroups-integration-test"* ]]
365
366 # check that the cgroups v2 path is the same for both processes
367 [[ "$run_cgroup" == "$exec_cgroup" ]]
368}
369
370@test "runc exec should refuse a paused container" {
371 if [[ "$ROOTLESS" -ne 0 ]]; then
372 requires rootless_cgroup
373 fi
374 requires cgroups_freezer
375
376 set_cgroups_path
377
378 runc run -d --console-socket "$CONSOLE_SOCKET" ct1
379 [ "$status" -eq 0 ]
380 runc pause ct1
381 [ "$status" -eq 0 ]
382
383 # Exec should not timeout or succeed.
384 runc exec ct1 echo ok
385 [ "$status" -eq 255 ]
386 [[ "$output" == *"cannot exec in a paused container"* ]]
387}
388
389@test "runc exec --ignore-paused" {
390 if [[ "$ROOTLESS" -ne 0 ]]; then
391 requires rootless_cgroup
392 fi
393 requires cgroups_freezer
394
395 set_cgroups_path
396
397 runc run -d --console-socket "$CONSOLE_SOCKET" ct1
398 [ "$status" -eq 0 ]
399 runc pause ct1
400 [ "$status" -eq 0 ]
401
402 # Resume the container a bit later.
403 (
404 sleep 2
405 runc resume ct1
406 ) &
407
408 # Exec should not timeout or succeed.
409 runc exec --ignore-paused ct1 echo ok
410 [ "$status" -eq 0 ]
411 [ "$output" = "ok" ]
412}
413
414@test "runc run/create should error/warn about a non-empty cgroup" {
415 if [[ "$ROOTLESS" -ne 0 ]]; then
416 requires rootless_cgroup
417 fi
418
419 set_cgroups_path
420
421 runc run -d --console-socket "$CONSOLE_SOCKET" ct1
422 [ "$status" -eq 0 ]
423
424 # When systemd driver is used, runc can't add PID to an existing unit,
425 # so runc returns an error. For backward compatibility, we still allow
426 # such configuration in 1.1, but only when systemd driver is NOT used.
427 # See https://github.com/opencontainers/runc/issues/3780.
428 local exp=0
429 [[ -n "${RUNC_USE_SYSTEMD}" ]] && exp=1
430
431 # Run a second container sharing the cgroup with the first one.
432 runc --debug run -d --console-socket "$CONSOLE_SOCKET" ct2
433 [ "$status" -eq "$exp" ]
434 [[ "$output" == *"container's cgroup is not empty"* ]]
435
436 # Same but using runc create.
437 runc create --console-socket "$CONSOLE_SOCKET" ct3
438 [ "$status" -eq "$exp" ]
439 [[ "$output" == *"container's cgroup is not empty"* ]]
440}
441
442@test "runc run/create should refuse pre-existing frozen cgroup" {
443 requires cgroups_freezer
444 if [[ "$ROOTLESS" -ne 0 ]]; then
445 requires rootless_cgroup
446 fi
447
448 set_cgroups_path
449
450 case $CGROUP_UNIFIED in
451 no)
452 FREEZER_DIR="${CGROUP_FREEZER_BASE_PATH}/${REL_CGROUPS_PATH}"
453 FREEZER="${FREEZER_DIR}/freezer.state"
454 STATE="FROZEN"
455 ;;
456 yes)
457 FREEZER_DIR="${CGROUP_PATH}"
458 FREEZER="${FREEZER_DIR}/cgroup.freeze"
459 STATE="1"
460 ;;
461 esac
462
463 # Create and freeze the cgroup.
464 mkdir -p "$FREEZER_DIR"
465 echo "$STATE" >"$FREEZER"
466
467 # Start a container.
468 runc run -d --console-socket "$CONSOLE_SOCKET" ct1
469 [ "$status" -eq 1 ]
470 # A warning should be printed.
471 [[ "$output" == *"container's cgroup unexpectedly frozen"* ]]
472
473 # Same check for runc create.
474 runc create --console-socket "$CONSOLE_SOCKET" ct2
475 [ "$status" -eq 1 ]
476 # A warning should be printed.
477 [[ "$output" == *"container's cgroup unexpectedly frozen"* ]]
478
479 # Cleanup.
480 rmdir "$FREEZER_DIR"
481}
View as plain text