fix(cli): detect containerd/CRI cgroup-v2 containers in is_container() (#47131)

Closes #47111

is_container() only recognized Docker (/.dockerenv), Podman
(/run/.containerenv), and docker/podman/lxc markers in /proc/1/cgroup.
Under cgroup v2 (Kubernetes/k3s on containerd or CRI-O) /proc/1/cgroup
collapses to a single "0::/" line with no runtime marker, so
is_container() returned False on every containerd/CRI pod.

That false negative bypassed container-aware behavior across the CLI.
The most damaging case (reported): even after #46290 fixed
detect_service_manager() to gate on _s6_running() alone, other
is_container() call sites (profile home resolution, gateway behaviors,
config, doctor) still misbehave on containerd.

Broaden detection conservatively:
- KUBERNETES_SERVICE_HOST env var (present in every k8s pod).
- kubepods/containerd/crio markers in /proc/1/cgroup (cgroup v1 nested).
- same markers in /proc/self/mountinfo as a cgroup-v2 fallback.

Tests: 3 new (k8s env, kubepods cgroup, cgroup-v2-via-mountinfo) plus the
existing negative case hardened to stub mountinfo + env; 108 constants +
service_manager tests pass.
This commit is contained in:
Bartok
2026-06-17 12:11:31 +10:00
committed by GitHub
parent 36ae958473
commit 5e01a5dbf1
2 changed files with 86 additions and 6 deletions
+55 -1
View File
@@ -139,12 +139,66 @@ class TestIsContainer:
"""Returns False on a regular Linux host."""
import builtins
self._reset_cache(monkeypatch)
monkeypatch.delenv("KUBERNETES_SERVICE_HOST", raising=False)
monkeypatch.setattr(os.path, "exists", lambda p: False)
cgroup_file = tmp_path / "cgroup"
cgroup_file.write_text("12:memory:/\n")
mountinfo_file = tmp_path / "mountinfo"
mountinfo_file.write_text("22 21 0:20 / /sys rw shared:7 - sysfs sysfs rw\n")
_real_open = builtins.open
def _fake_open(p, *a, **kw):
if p == "/proc/1/cgroup":
return _real_open(str(cgroup_file), *a, **kw)
if p == "/proc/self/mountinfo":
return _real_open(str(mountinfo_file), *a, **kw)
return _real_open(p, *a, **kw)
monkeypatch.setattr("builtins.open", _fake_open)
assert is_container() is False
def test_detects_kubernetes_env(self, monkeypatch):
"""KUBERNETES_SERVICE_HOST env var triggers detection (k8s/k3s pod)."""
self._reset_cache(monkeypatch)
monkeypatch.setattr(os.path, "exists", lambda p: False)
monkeypatch.setenv("KUBERNETES_SERVICE_HOST", "10.43.0.1")
assert is_container() is True
def test_detects_cgroup_kubepods(self, monkeypatch, tmp_path):
"""/proc/1/cgroup containing 'kubepods' triggers detection."""
import builtins
self._reset_cache(monkeypatch)
monkeypatch.delenv("KUBERNETES_SERVICE_HOST", raising=False)
monkeypatch.setattr(os.path, "exists", lambda p: False)
cgroup_file = tmp_path / "cgroup"
cgroup_file.write_text("12:memory:/kubepods/besteffort/podabc\n")
_real_open = builtins.open
monkeypatch.setattr("builtins.open", lambda p, *a, **kw: _real_open(str(cgroup_file), *a, **kw) if p == "/proc/1/cgroup" else _real_open(p, *a, **kw))
assert is_container() is False
assert is_container() is True
def test_detects_cgroup_v2_via_mountinfo(self, monkeypatch, tmp_path):
"""cgroup v2 (0::/ only) falls back to containerd marker in mountinfo."""
import builtins
self._reset_cache(monkeypatch)
monkeypatch.delenv("KUBERNETES_SERVICE_HOST", raising=False)
monkeypatch.setattr(os.path, "exists", lambda p: False)
cgroup_file = tmp_path / "cgroup"
cgroup_file.write_text("0::/\n") # cgroup v2 — no runtime marker
mountinfo_file = tmp_path / "mountinfo"
mountinfo_file.write_text(
"1234 1233 0:42 /containerd/.../rootfs / rw - overlay overlay rw\n"
)
_real_open = builtins.open
def _fake_open(p, *a, **kw):
if p == "/proc/1/cgroup":
return _real_open(str(cgroup_file), *a, **kw)
if p == "/proc/self/mountinfo":
return _real_open(str(mountinfo_file), *a, **kw)
return _real_open(p, *a, **kw)
monkeypatch.setattr("builtins.open", _fake_open)
assert is_container() is True
def test_caches_result(self, monkeypatch):
"""Second call uses cached value without re-probing."""