...

Text file src/github.com/opencontainers/runc/tests/integration/cgroups.bats

Documentation: github.com/opencontainers/runc/tests/integration

     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