Skip to content

Commit 2769dff

Browse files
committed
test: match collector fixture ownership on Linux
1 parent 5e79389 commit 2769dff

1 file changed

Lines changed: 78 additions & 2 deletions

File tree

‎tools/ci/tests/test_collector_metric_privacy.py‎

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,42 @@ def require_cached_collector_image(image):
166166
)
167167

168168

169+
def model_loading_container_user(directory):
170+
owner = directory.stat()
171+
return f"--user={owner.st_uid}:{owner.st_gid}"
172+
173+
174+
def model_loading_startup_diagnostics(container):
175+
results = []
176+
commands = (
177+
[
178+
"docker",
179+
"inspect",
180+
"--format",
181+
"{{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}}",
182+
container,
183+
],
184+
["docker", "logs", "--tail", "20", container],
185+
)
186+
for command in commands:
187+
try:
188+
result = subprocess.run( # noqa: S603 - fixed diagnostics for the exact test-owned container
189+
command, check=False, capture_output=True, text=True, errors="replace", timeout=5
190+
)
191+
results.append(f"{command[1]} exit={result.returncode}: {(result.stdout + result.stderr)[-4096:]}")
192+
except (OSError, subprocess.SubprocessError) as error:
193+
results.append(f"{command[1]} unavailable: {type(error).__name__}")
194+
return "Collector startup diagnostics:\n" + "\n".join(results)
195+
196+
197+
def wait_for_model_loading_collector(container, health_check):
198+
try:
199+
wait_for(health_check)
200+
except AssertionError as error:
201+
error.add_note(model_loading_startup_diagnostics(container))
202+
raise
203+
204+
169205
def cleanup_model_loading_collector(container, original_error):
170206
try:
171207
removed = subprocess.run( # noqa: S603 - exact test-owned container
@@ -206,6 +242,46 @@ def missing(*args):
206242
assert calls == [("docker", "image", "inspect", image)]
207243

208244

245+
def test_model_loading_container_uses_private_fixture_owner(tmp_path):
246+
tmp_path.chmod(0o700)
247+
owner = tmp_path.stat()
248+
assert model_loading_container_user(tmp_path) == f"--user={owner.st_uid}:{owner.st_gid}"
249+
assert owner.st_mode & 0o777 == 0o700
250+
251+
252+
@pytest.mark.parametrize("diagnostics_fail", [False, True])
253+
def test_model_loading_startup_failure_retains_bounded_diagnostics(monkeypatch, diagnostics_fail):
254+
original_error = AssertionError("collector not ready")
255+
calls = []
256+
257+
def failed_wait(_predicate):
258+
raise original_error
259+
260+
def fake_run(args, **kwargs):
261+
calls.append((args, kwargs))
262+
if diagnostics_fail:
263+
raise subprocess.TimeoutExpired(args, kwargs["timeout"])
264+
raw_output = b"exited exit=1 oom=false" if args[1] == "inspect" else b"x" * 8192 + b"permission denied\xff"
265+
output = raw_output.decode("utf-8", errors=kwargs.get("errors", "strict"))
266+
return subprocess.CompletedProcess(args, 0, output, "")
267+
268+
monkeypatch.setitem(wait_for_model_loading_collector.__globals__, "wait_for", failed_wait)
269+
monkeypatch.setattr(subprocess, "run", fake_run)
270+
with pytest.raises(AssertionError) as caught:
271+
wait_for_model_loading_collector("sie-model-loading-test", lambda: True)
272+
assert caught.value is original_error
273+
(note,) = caught.value.__notes__
274+
assert len(note) < 8300
275+
assert "TimeoutExpired" in note if diagnostics_fail else "exited exit=1 oom=false" in note
276+
if not diagnostics_fail:
277+
assert "permission denied" in note
278+
assert "\ufffd" in note
279+
assert [args[1] for args, _ in calls] == ["inspect", "logs"]
280+
assert calls[1][0] == ["docker", "logs", "--tail", "20", "sie-model-loading-test"]
281+
assert all(args[-1] == "sie-model-loading-test" and kwargs["timeout"] == 5 for args, kwargs in calls)
282+
assert all(kwargs["errors"] == "replace" for _, kwargs in calls)
283+
284+
209285
@pytest.mark.parametrize("removed", [False, True])
210286
@pytest.mark.parametrize("original_error", [None, RuntimeError("startup or test failure")])
211287
def test_model_loading_cleanup_preserves_failures(monkeypatch, removed, original_error):
@@ -284,7 +360,7 @@ def test_model_loading_gauge_preserves_prometheus_name_and_remote_unit(tmp_path)
284360
"--cap-drop=ALL",
285361
"--security-opt=no-new-privileges",
286362
"--read-only",
287-
"--user=0",
363+
model_loading_container_user(tmp_path),
288364
"-p",
289365
"127.0.0.1::4338",
290366
"-p",
@@ -302,7 +378,7 @@ def get(port, path=""):
302378
with urllib.request.urlopen(f"http://127.0.0.1:{ports[port]}{path}", timeout=2) as response:
303379
return response.read(1_048_577).decode()
304380

305-
wait_for(lambda: get(13133))
381+
wait_for_model_loading_collector(container, lambda: get(13133))
306382
request = urllib.request.Request(
307383
f"http://127.0.0.1:{ports[4338]}/v1/metrics",
308384
data=model_loading_payload(),

0 commit comments

Comments
 (0)