Skip to content

Fix: remove hard coded cgroup path in nerdctl info - #5182

Open
chemwolf6922 wants to merge 1 commit into
containerd:mainfrom
chemwolf6922:rootless-systemd-cgroup-discovery
Open

Fix: remove hard coded cgroup path in nerdctl info#5182
chemwolf6922 wants to merge 1 commit into
containerd:mainfrom
chemwolf6922:rootless-systemd-cgroup-discovery

Conversation

@chemwolf6922

@chemwolf6922 chemwolf6922 commented Sep 4, 2026

Copy link
Copy Markdown

Summary

Rootless cgroup-v2 capability detection assumed the systemd user hierarchy started directly under /sys/fs/cgroup/user.slice. This fails when systemd places the user manager under a prefixed or nested hierarchy, causing nerdctl info and the rootless setup tool to report incorrect controller availability.

This PR resolves the systemd user manager's actual cgroup over D-Bus instead.

Resolves #5172

Tests

Testcases

  1. nerdctl info reports memory limit support correctly.
  2. nerdctl info reports pids limit support correctly.
  3. nerdctl info reports cpu limit support correctly.
  4. The setup script check correctly reports cgroup support.
  5. no-limit rootless container runs.
  6. cpu limit is correctly applied to a rootless container.
  7. memory limit is correctly applied to a rootless container.
  8. pids limit is correctly applied to a rootless container.
  9. The rootless container's cgroup hierarchy is correct.

Test platforms

The pre-fix nerdctl is built from release 2.3.5.

name distro kernel systemd
WSL Ubuntu 26.04 LTS (Resolute Raccoon) 6.18.40.1-microsoft-standard-WSL2 259.5-0ubuntu3.4
Ubuntu Ubuntu 24.04.4 LTS (Noble Numbat) 6.8.0-110-generic 255.4-1ubuntu8.15
Fedora Fedora Linux 44 (Server Edition) 6.19.10-300.fc44.x86_64 259.5-1.fc44
Arch Arch Linux rolling release 7.2.2-arch1-1 261.2-1-arch

Test results

Before the fix

Testcase info memory info pids info cpu setup check basic cpu limit memory limit pid limit cgroup hierarchy
WSL fail fail fail fail pass pass pass pass pass
Ubuntu pass pass pass pass pass pass pass pass pass
Fedora pass pass pass pass pass pass pass pass pass
Arch pass pass pass pass pass pass pass pass pass

After the fix

Testcase info memory info pids info cpu setup check basic cpu limit memory limit pid limit cgroup hierarchy
WSL pass pass pass pass pass pass pass pass pass
Ubuntu pass pass pass pass pass pass pass pass pass
Fedora pass pass pass pass pass pass pass pass pass
Arch pass pass pass pass pass pass pass pass pass

The actual run results all pass before and after the fix as those are handled by containerd and runc. And those already handle this correctly.

Test script

#!/usr/bin/env bash
#
# End-to-end test for rootless nerdctl with the systemd cgroup manager.
#
# This script starts a temporary, isolated rootless containerd. It does not
# replace, stop, reconfigure, or use the data of an existing containerd.
#
# Usage:
#   ./rootless-cgroup-systemd-nerdctl.sh             # installed nerdctl
#   ./rootless-cgroup-systemd-nerdctl.sh ./nerdctl   # specified binary
#   ./rootless-cgroup-systemd-nerdctl.sh --build     # build this checkout
#
# TEST_IMAGE may name a different image, but it must provide /bin/sh, cat, and
# sleep. The default is busybox:latest. If Docker already has the image, the
# script copies it into the temporary containerd; otherwise nerdctl pulls it.
#
set -Eeuo pipefail

if [[ -t 1 && ${TERM:-dumb} != dumb ]]; then
	CYAN=$'\033[36m'
	GREEN=$'\033[32m'
	YELLOW=$'\033[33m'
	RESET=$'\033[0m'
else
	CYAN=
	GREEN=
	YELLOW=
	RESET=
fi
if [[ -t 2 && ${TERM:-dumb} != dumb ]]; then
	RED=$'\033[31m'
	ERROR_RESET=$'\033[0m'
else
	RED=
	ERROR_RESET=
fi

info() { printf '%sINFO:%s %s\n' "$CYAN" "$RESET" "$*"; }
pass() { printf '%sPASS:%s %s\n' "$GREEN" "$RESET" "$*"; }
skip() { printf '%sSKIP:%s %s\n' "$YELLOW" "$RESET" "$*"; }
fail() {
	FAIL_REPORTED=1
	printf '%sFAIL: %s%s\n' "$RED" "$*" "$ERROR_RESET" >&2
	exit 1
}
test_fail() {
	((TEST_FAILURES += 1))
	printf '%sFAIL: %s%s\n' "$RED" "$*" "$ERROR_RESET" >&2
}

require_command() {
	command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}

has_controller() {
	[[ " $CONTROLLERS " == *" $1 "* ]]
}

usage() {
	cat <<'EOF'
Usage:
  rootless-cgroup-systemd-nerdctl.sh [NERDCTL]
  rootless-cgroup-systemd-nerdctl.sh --build

With no argument, test the nerdctl found in PATH.
With a path, test that nerdctl binary.
With --build, build ./cmd/nerdctl from the current nerdctl checkout.
EOF
}

case "${1:-}" in
	-h|--help)
		usage
		exit 0
		;;
	--build)
		BUILD_FROM_CHECKOUT=1
		;;
	"")
		BUILD_FROM_CHECKOUT=0
		NERDCTL_SOURCE=$(command -v nerdctl || true)
		;;
	*)
		BUILD_FROM_CHECKOUT=0
		NERDCTL_SOURCE=$1
		;;
esac

[[ $(id -u) -ne 0 ]] || fail "run this test as a non-root user"

for command in containerd ctr runc systemctl systemd-run rootlesskit \
	containerd-rootless.sh nsenter tar sha256sum; do
	require_command "$command"
done

if command -v slirp4netns >/dev/null 2>&1; then
	ROOTLESS_NETWORK=slirp4netns
	ROOTLESS_PORT_DRIVER=builtin
elif command -v pasta >/dev/null 2>&1; then
	ROOTLESS_NETWORK=pasta
	ROOTLESS_PORT_DRIVER=implicit
else
	fail "install slirp4netns or pasta before running this test"
fi

[[ -f /sys/fs/cgroup/cgroup.controllers ]] ||
	fail "this test requires cgroup v2 mounted at /sys/fs/cgroup"
systemctl --user show-environment >/dev/null 2>&1 ||
	fail "a working systemd user session is required"

RUNTIME_DIR=${XDG_RUNTIME_DIR:-"/run/user/$(id -u)"}
[[ -d "$RUNTIME_DIR" && -w "$RUNTIME_DIR" ]] ||
	fail "runtime directory is not writable: $RUNTIME_DIR"

TEST_ID="$$"
UNIT="nerdctl-rootless-cgroup-test-${TEST_ID}.service"
WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/nerdctl-rootless-cgroup-test.XXXXXX")
NERDCTL_UNDER_TEST="$WORK_DIR/nerdctl"
STATE_DIR="$RUNTIME_DIR/nerdctl-rootlesskit-test-${TEST_ID}"
TEST_XDG_DATA_HOME="$WORK_DIR/xdg-data"
TEST_XDG_CONFIG_HOME="$WORK_DIR/xdg-config"
NERDCTL_DATA_ROOT="$WORK_DIR/nerdctl-data"
NAMESPACE="nerdctl-cgroup-test-${TEST_ID}"
IMAGE=${TEST_IMAGE:-busybox:latest}
UNIT_STARTED=0
CONTAINER_ID=""
FAIL_REPORTED=0
TEST_FAILURES=0

mkdir -p "$TEST_XDG_DATA_HOME" "$TEST_XDG_CONFIG_HOME/containerd" "$NERDCTL_DATA_ROOT"

case "$(uname -m)" in
	x86_64)
	TEST_PLATFORM=linux/amd64
	;;
	aarch64|arm64)
	TEST_PLATFORM=linux/arm64
	;;
	*)
	fail "unsupported architecture for native snapshotter test: $(uname -m)"
	;;
esac

cat >"$TEST_XDG_CONFIG_HOME/containerd/config.toml" <<EOF
version = 3

[[plugins."io.containerd.transfer.v1.local".unpack_config]]
  platform = "$TEST_PLATFORM"
  snapshotter = "native"
EOF

# Every invocation is pinned to the temporary RootlessKit state, containerd
# namespace, native snapshotter, and nerdctl data root.
test_nerdctl() {
	ROOTLESSKIT_STATE_DIR="$STATE_DIR" \
		"$NERDCTL_UNDER_TEST" \
		--namespace="$NAMESPACE" \
		--snapshotter=native \
		--data-root="$NERDCTL_DATA_ROOT" \
		--cgroup-manager=systemd \
		"$@"
}

daemon_error_summary() {
	journalctl --user-unit="$UNIT" --no-pager -o cat -n 50 2>/dev/null |
		grep -Ei '(^|[[:space:]])(error|failed|failure)([=:[:space:]]|$)' |
		tail -n 10 || true
}

cleanup() {
	status=$?
	trap - EXIT
	set +e

	if [[ -n "$CONTAINER_ID" && $UNIT_STARTED -eq 1 ]]; then
		test_nerdctl rm -f "$CONTAINER_ID" >/dev/null 2>&1 || true
	fi
	if [[ $UNIT_STARTED -eq 1 ]]; then
		systemctl --user stop "$UNIT" >/dev/null 2>&1 || true
		systemctl --user reset-failed "$UNIT" >/dev/null 2>&1 || true
	fi
	rm -rf "$STATE_DIR"

	# Snapshot files can be owned by subordinate UIDs. Remove them from a
	# matching user namespace, then fall back to normal removal.
	if [[ -e "$WORK_DIR" ]]; then
		rootlesskit --net=host --disable-host-loopback=false \
			/bin/rm -rf "$WORK_DIR" >/dev/null 2>&1 ||
			rm -rf "$WORK_DIR" >/dev/null 2>&1 || true
	fi

	if ((TEST_FAILURES > 0)); then
		printf '%sFAIL: %d behavioral test(s) failed; temporary resources were removed%s\n' \
			"$RED" "$TEST_FAILURES" "$ERROR_RESET" >&2
	elif [[ $status -eq 0 ]]; then
		printf '%sPASS:%s all applicable tests passed; temporary resources were removed\n' \
			"$GREEN" "$RESET"
	elif [[ $FAIL_REPORTED -eq 0 ]]; then
		printf '%sFAIL: unexpected command failure (exit status %d)%s\n' \
			"$RED" "$status" "$ERROR_RESET" >&2
	fi
	exit "$status"
}
trap cleanup EXIT

if [[ $BUILD_FROM_CHECKOUT -eq 1 ]]; then
	require_command go
	[[ -f go.mod ]] || fail "--build must be run from a nerdctl Git checkout"
	grep -q '^module github.com/containerd/nerdctl/v2$' go.mod ||
		fail "--build must be run from the root of a nerdctl Git checkout"
	CGO_ENABLED=0 go build -o "$NERDCTL_UNDER_TEST" ./cmd/nerdctl
	SETUPTOOL_UNDER_TEST=$(readlink -f extras/rootless/containerd-rootless-setuptool.sh)
else
	[[ -n "${NERDCTL_SOURCE:-}" ]] || fail "nerdctl was not found in PATH"
	[[ -x "$NERDCTL_SOURCE" ]] || fail "nerdctl is not executable: $NERDCTL_SOURCE"
	NERDCTL_SOURCE=$(readlink -f "$NERDCTL_SOURCE")
	cp "$NERDCTL_SOURCE" "$NERDCTL_UNDER_TEST"
	chmod 0755 "$NERDCTL_UNDER_TEST"
	SETUPTOOL_UNDER_TEST="$(dirname "$NERDCTL_SOURCE")/containerd-rootless-setuptool.sh"
fi
[[ -x "$SETUPTOOL_UNDER_TEST" ]] ||
	fail "matching containerd-rootless-setuptool.sh is not executable: $SETUPTOOL_UNDER_TEST"

NERDCTL_VERSION=$("$NERDCTL_UNDER_TEST" --version)
NERDCTL_SHA256=$(sha256sum "$NERDCTL_UNDER_TEST" | awk '{print $1}')
CONTAINERD_VERSION=$(containerd --version)
info "nerdctl under test: $NERDCTL_VERSION (SHA-256: $NERDCTL_SHA256)"
info "containerd: $CONTAINERD_VERSION"
info "setup tool under test: $SETUPTOOL_UNDER_TEST"

MANAGER_CGROUP=$(SYSTEMD_PAGER=cat systemctl --user show --value --property=ControlGroup)
[[ "$MANAGER_CGROUP" == /* ]] || fail "invalid systemd user-manager cgroup: $MANAGER_CGROUP"
MANAGER_CONTROLLER_FILE="/sys/fs/cgroup${MANAGER_CGROUP}/cgroup.controllers"
[[ -r "$MANAGER_CONTROLLER_FILE" ]] ||
	fail "systemd user-manager controller file is missing: $MANAGER_CONTROLLER_FILE"
MANAGER_CONTROLLERS=$(<"$MANAGER_CONTROLLER_FILE")
OLD_FIXED_PATH="/sys/fs/cgroup/user.slice/user-$(id -u).slice/cgroup.controllers"
[[ -e "$OLD_FIXED_PATH" ]] && OLD_FIXED_PATH_STATE=present || OLD_FIXED_PATH_STATE=absent

set +e
SETUPTOOL_OUTPUT=$("$SETUPTOOL_UNDER_TEST" check 2>&1)
SETUPTOOL_STATUS=$?
set -e
SETUPTOOL_CGROUP_OUTPUT=$(awk '
	/Checking cgroup v2/ { capture = 1 }
	capture { print }
	/Checking overlayfs/ { exit }
' <<<"$SETUPTOOL_OUTPUT")
SETUPTOOL_CONTROLLERS_OK=1
if [[ $SETUPTOOL_STATUS -ne 0 ]]; then
	test_fail "containerd-rootless-setuptool.sh check failed:"$'\n'"$SETUPTOOL_OUTPUT"
	SETUPTOOL_CONTROLLERS_OK=0
elif grep -Eq 'Enabling cgroup v2 is highly recommended|Unable to inspect cgroup v2 controllers' \
	<<<"$SETUPTOOL_CGROUP_OUTPUT"; then
	test_fail "setup tool could not inspect the systemd user-manager controllers:"$'\n'"$SETUPTOOL_CGROUP_OUTPUT"
	SETUPTOOL_CONTROLLERS_OK=0
else
	for controller in cpu memory pids; do
		if [[ " $MANAGER_CONTROLLERS " == *" $controller "* ]]; then
			if grep -Fq "controller \"$controller\" is not delegated" <<<"$SETUPTOOL_CGROUP_OUTPUT"; then
				test_fail "setup tool did not recognize the delegated $controller controller:"$'\n'"$SETUPTOOL_CGROUP_OUTPUT"
				SETUPTOOL_CONTROLLERS_OK=0
			fi
		elif ! grep -Fq "controller \"$controller\" is not delegated" <<<"$SETUPTOOL_CGROUP_OUTPUT"; then
			test_fail "setup tool did not warn that $controller is not delegated:"$'\n'"$SETUPTOOL_CGROUP_OUTPUT"
			SETUPTOOL_CONTROLLERS_OK=0
		fi
	done
fi
if [[ $SETUPTOOL_CONTROLLERS_OK -eq 1 ]]; then
	pass "setup tool reports the systemd user-manager controllers correctly"
fi

if ! SYSTEMD_PAGER=cat systemd-run \
	--user \
	--collect \
	--unit="$UNIT" \
	--description="Temporary nerdctl rootless cgroup test" \
	--property=Delegate=yes \
	/usr/bin/env \
	CONTAINERD_ROOTLESS_ROOTLESSKIT_STATE_DIR="$STATE_DIR" \
	CONTAINERD_ROOTLESS_ROOTLESSKIT_NET="$ROOTLESS_NETWORK" \
	CONTAINERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER="$ROOTLESS_PORT_DRIVER" \
	CONTAINERD_ROOTLESS_ROOTLESSKIT_DETACH_NETNS=false \
	XDG_DATA_HOME="$TEST_XDG_DATA_HOME" \
	XDG_CONFIG_HOME="$TEST_XDG_CONFIG_HOME" \
	XDG_RUNTIME_DIR="$RUNTIME_DIR" \
	DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-unix:path=$RUNTIME_DIR/bus}" \
	HOME="$HOME" \
	PATH="$PATH" \
	"$(command -v containerd-rootless.sh)" \
	--log-level=error >"$WORK_DIR/systemd-run.log" 2>&1; then
	fail "could not start the temporary systemd service:"$'\n'"$(tail -n 10 "$WORK_DIR/systemd-run.log")"
fi
UNIT_STARTED=1

DAEMON_READY=0
CONTAINERD_PID=""
CONTAINERD_ADDRESS=""
for _ in $(seq 1 300); do
	if [[ -s "$STATE_DIR/child_pid" ]]; then
		CONTAINERD_PID=$(<"$STATE_DIR/child_pid")
		if [[ "$CONTAINERD_PID" =~ ^[0-9]+$ ]]; then
			CONTAINERD_ADDRESS="/proc/$CONTAINERD_PID/root/run/containerd/containerd.sock"
			if ctr --address "$CONTAINERD_ADDRESS" version >/dev/null 2>&1; then
				DAEMON_READY=1
				break
			fi
		fi
	fi
	sleep 0.1
done
if [[ $DAEMON_READY -ne 1 ]]; then
	DAEMON_ERRORS=$(daemon_error_summary)
	if [[ -n "$DAEMON_ERRORS" ]]; then
		fail "the temporary rootless containerd did not become ready:"$'\n'"$DAEMON_ERRORS"
	fi
	fail "the temporary rootless containerd did not become ready"
fi
info "temporary isolated rootless containerd started"

[[ -r "/proc/$CONTAINERD_PID/cgroup" ]] ||
	fail "invalid or missing containerd process: $CONTAINERD_PID"
DAEMON_CGROUP=$(cut -d: -f3 "/proc/$CONTAINERD_PID/cgroup")
[[ "$DAEMON_CGROUP" == /* ]] || fail "invalid containerd cgroup: $DAEMON_CGROUP"
CONTROLLER_FILE="/sys/fs/cgroup${DAEMON_CGROUP}/cgroup.controllers"
[[ -r "$CONTROLLER_FILE" ]] || fail "controller file is missing: $CONTROLLER_FILE"
CONTROLLERS=$(<"$CONTROLLER_FILE")

[[ "$DAEMON_CGROUP" == "$MANAGER_CGROUP/"* ]] ||
	fail "containerd is not below the systemd user-manager cgroup"
info "containerd cgroup: $DAEMON_CGROUP"
info "delegated controllers: ${CONTROLLERS:-none}; old fixed path: $OLD_FIXED_PATH_STATE"

if ! DRIVER=$(test_nerdctl info --format '{{.CgroupDriver}}'); then
	fail "nerdctl info failed"
fi
CGROUP_VERSION=$(test_nerdctl info --format '{{.CgroupVersion}}')
SECURITY_OPTIONS=$(test_nerdctl info --format '{{json .SecurityOptions}}')
INFO_MEMORY=$(test_nerdctl info --format '{{.MemoryLimit}}')
INFO_PIDS=$(test_nerdctl info --format '{{.PidsLimit}}')
INFO_CPU_QUOTA=$(test_nerdctl info --format '{{.CPUCfsQuota}}')
INFO_CPU_PERIOD=$(test_nerdctl info --format '{{.CPUCfsPeriod}}')

[[ "$DRIVER" == systemd ]] || fail "expected systemd cgroup manager, got $DRIVER"
[[ "$CGROUP_VERSION" == 2 ]] || fail "expected cgroup v2, got $CGROUP_VERSION"
[[ "$SECURITY_OPTIONS" == *rootless* ]] || fail "nerdctl does not report rootless mode"

if has_controller memory; then
	if [[ "$INFO_MEMORY" == true ]]; then
		pass "nerdctl info reports memory limit support"
	else
		test_fail "nerdctl info does not report memory limit support"
	fi
elif [[ "$INFO_MEMORY" == false ]]; then
	pass "nerdctl info does not report unavailable memory support"
else
	test_fail "nerdctl info unexpectedly reports memory limit support"
fi

if has_controller pids; then
	if [[ "$INFO_PIDS" == true ]]; then
		pass "nerdctl info reports PIDs limit support"
	else
		test_fail "nerdctl info does not report PIDs limit support"
	fi
elif [[ "$INFO_PIDS" == false ]]; then
	pass "nerdctl info does not report unavailable PIDs support"
else
	test_fail "nerdctl info unexpectedly reports PIDs limit support"
fi

if has_controller cpu; then
	if [[ "$INFO_CPU_QUOTA" == true && "$INFO_CPU_PERIOD" == true ]]; then
		pass "nerdctl info reports CPU limit support"
	else
		test_fail "nerdctl info does not report CPU limit support"
	fi
elif [[ "$INFO_CPU_QUOTA" == false && "$INFO_CPU_PERIOD" == false ]]; then
	pass "nerdctl info does not report unavailable CPU support"
else
	test_fail "nerdctl info unexpectedly reports CPU limit support"
fi

if command -v docker >/dev/null 2>&1 && docker image inspect "$IMAGE" >/dev/null 2>&1; then
	if ! docker image save "$IMAGE" | test_nerdctl load >/dev/null; then
		fail "could not copy $IMAGE from the existing Docker daemon"
	fi
else
	if ! test_nerdctl pull "$IMAGE" >"$WORK_DIR/image-pull.log" 2>&1; then
		fail "could not obtain $IMAGE; pre-pull it with Docker or ensure the temporary containerd has network access:"$'\n'"$(tail -n 10 "$WORK_DIR/image-pull.log")"
	fi
fi
info "test image is available in the temporary containerd"

set +e
NO_LIMIT_OUTPUT=$(test_nerdctl run --rm --net=none "$IMAGE" /bin/sh -c 'echo no-limit-container-succeeded' 2>&1)
NO_LIMIT_STATUS=$?
set -e
if [[ $NO_LIMIT_STATUS -ne 0 ]]; then
	test_fail "no-limit rootless container:"$'\n'"$NO_LIMIT_OUTPUT"
elif [[ "$NO_LIMIT_OUTPUT" != *no-limit-container-succeeded* ]]; then
	test_fail "no-limit rootless container returned unexpected output:"$'\n'"$NO_LIMIT_OUTPUT"
else
	pass "no-limit rootless container"
fi

if has_controller cpu; then
	set +e
	CPU_OUTPUT=$(test_nerdctl run --rm --net=none --cpus=1 "$IMAGE" /bin/sh -c 'cat /sys/fs/cgroup/cpu.max' 2>&1)
	CPU_STATUS=$?
	set -e
	if [[ $CPU_STATUS -ne 0 ]]; then
		test_fail "CPU-limited container:"$'\n'"$CPU_OUTPUT"
	elif [[ -z "$CPU_OUTPUT" || "$CPU_OUTPUT" == max* ]]; then
		test_fail "CPU quota was not applied: $CPU_OUTPUT"
	else
		pass "CPU limit applied"
	fi
else
	set +e
	CPU_ERROR=$(test_nerdctl run --rm --net=none --cpus=1 "$IMAGE" /bin/sh -c true 2>&1)
	CPU_STATUS=$?
	set -e
	CPU_ERROR_LOWER=${CPU_ERROR,,}
	if [[ $CPU_STATUS -eq 0 ]]; then
		test_fail "--cpus succeeded even though cpu is not delegated"
	elif [[ "$CPU_ERROR_LOWER" != *cpu* && "$CPU_ERROR_LOWER" != *controller* ]]; then
		test_fail "--cpus was rejected for an unexpected reason:"$'\n'"$CPU_ERROR"
	else
		pass "CPU limit correctly rejected because cpu is not delegated"
	fi
fi

if has_controller memory; then
	set +e
	MEMORY_OUTPUT=$(test_nerdctl run --rm --net=none --memory=16m "$IMAGE" /bin/sh -c 'cat /sys/fs/cgroup/memory.max' 2>&1)
	MEMORY_STATUS=$?
	set -e
	if [[ $MEMORY_STATUS -ne 0 ]]; then
		test_fail "memory-limited container:"$'\n'"$MEMORY_OUTPUT"
	elif [[ "$MEMORY_OUTPUT" != 16777216 ]]; then
		test_fail "memory limit was not applied: expected 16777216, got $MEMORY_OUTPUT"
	else
		pass "memory limit applied"
	fi
else
	skip "memory is not delegated"
fi

if has_controller pids; then
	set +e
	PIDS_OUTPUT=$(test_nerdctl run --rm --net=none --pids-limit=16 "$IMAGE" /bin/sh -c 'cat /sys/fs/cgroup/pids.max' 2>&1)
	PIDS_STATUS=$?
	set -e
	if [[ $PIDS_STATUS -ne 0 ]]; then
		test_fail "PIDs-limited container:"$'\n'"$PIDS_OUTPUT"
	elif [[ "$PIDS_OUTPUT" != 16 ]]; then
		test_fail "PIDs limit was not applied: expected 16, got $PIDS_OUTPUT"
	else
		pass "PIDs limit applied"
	fi
else
	skip "pids is not delegated"
fi

set +e
CONTAINER_OUTPUT=$(test_nerdctl run -d --net=none "$IMAGE" /bin/sh -c 'sleep 300' 2>&1)
CONTAINER_STATUS=$?
set -e
if [[ $CONTAINER_STATUS -ne 0 ]]; then
	test_fail "container systemd scope:"$'\n'"$CONTAINER_OUTPUT"
else
	CONTAINER_ID=$(tail -n 1 <<<"$CONTAINER_OUTPUT")
	set +e
	CONTAINER_PID=$(test_nerdctl inspect --format '{{.State.Pid}}' "$CONTAINER_ID" 2>/dev/null)
	INSPECT_STATUS=$?
	set -e
	if [[ $INSPECT_STATUS -ne 0 || ! "$CONTAINER_PID" =~ ^[0-9]+$ || ! -r "/proc/$CONTAINER_PID/cgroup" ]]; then
		test_fail "container systemd scope: invalid or missing container process: ${CONTAINER_PID:-unknown}"
	else
		CONTAINER_CGROUP=$(cut -d: -f3 "/proc/$CONTAINER_PID/cgroup")
		if [[ -z "$CONTROLLERS" ]]; then
			skip "no controllers are delegated, so no container cgroup is expected"
		elif [[ "$CONTAINER_CGROUP" != "$MANAGER_CGROUP/"* ]]; then
			test_fail "container scope did not retain the user-manager cgroup prefix: $CONTAINER_CGROUP"
		elif [[ "$(basename "$CONTAINER_CGROUP")" != nerdctl-*.scope ]]; then
			test_fail "container was not placed in a nerdctl systemd scope: $CONTAINER_CGROUP"
		else
			pass "container systemd scope: $CONTAINER_CGROUP"
		fi
	fi

	test_nerdctl rm -f "$CONTAINER_ID" >/dev/null 2>&1 || true
	CONTAINER_ID=""
fi

if ((TEST_FAILURES > 0)); then
	exit 1
fi

@chemwolf6922
chemwolf6922 force-pushed the rootless-systemd-cgroup-discovery branch from d548b9a to e05eee0 Compare September 4, 2026 05:01
@chemwolf6922

Copy link
Copy Markdown
Author

The test failures do not seem related to this change. Please help retrigger the tests. Thanks.

@AkihiroSuda AkihiroSuda added this to the v2.4.0 milestone Sep 4, 2026
@AkihiroSuda AkihiroSuda added the area/rootless Rootless mode label Sep 4, 2026
Comment thread extras/rootless/containerd-rootless-setuptool.sh Outdated
Comment thread extras/rootless/containerd-rootless-setuptool.sh Outdated
Comment thread extras/rootless/containerd-rootless-setuptool.sh Outdated
Comment thread pkg/infoutil/infoutil_linux.go Outdated
Comment thread go.mod Outdated
@chemwolf6922
chemwolf6922 force-pushed the rootless-systemd-cgroup-discovery branch from e05eee0 to 7ae6940 Compare September 4, 2026 08:32
@chemwolf6922

Copy link
Copy Markdown
Author

Hi @AkihiroSuda .
Thanks for the review. The original change was bloated with over designed junk. I have simplified the design. And should have resolved the concerns. Please help take a look. Thanks.

Comment thread pkg/infoutil/rootless_cgroup_linux.go Outdated
Comment thread pkg/infoutil/infoutil_linux.go Outdated
Comment thread pkg/infoutil/infoutil_linux.go
Signed-off-by: Feng Wang <wang6922@outlook.com>
@chemwolf6922
chemwolf6922 force-pushed the rootless-systemd-cgroup-discovery branch from 7ae6940 to 7bc523d Compare September 4, 2026 08:50
Comment thread pkg/infoutil/infoutil_linux.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/rootless Rootless mode

Projects

None yet

Development

Successfully merging this pull request may close these issues.

user.slice path must not be hardcoded (affects WSL2.9)

2 participants