Skip to content

Commit 0894494

Browse files
CodeLieutenantsoyacz
authored andcommitted
fix(client): forward run_id into ArgusGenericClient, uuid-harden replay log filenames
ArgusGenericClient (used by pytest-argus-reporter, and therefore dtest) never accepted or forwarded run_id to ArgusClient, unlike ArgusSCTClient and DriverMatrixTestsClient. Every replay log it created fell back to the "unknown" run-id placeholder regardless of ARGUS_RUN_ID. The ArgusReporter.argus_client cached_property also didn't pass run_id through, so both call sites needed the fix. Also replace the pid+process-counter uniqueness scheme in ReplayLog's filename suffix with a uuid4 component, since pid-based uniqueness doesn't hold when log_dir is a mount shared across hosts/containers with independent pid namespaces. Clamp the sanitized run_id to 64 chars to bound filename length.
1 parent f238b4d commit 0894494

5 files changed

Lines changed: 56 additions & 10 deletions

File tree

argus/client/generic/client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import logging
2+
from uuid import UUID
3+
24
from argus.client.base import ArgusClient
35

46

@@ -14,10 +16,11 @@ class Routes(ArgusClient.Routes):
1416

1517
def __init__(self, auth_token: str, base_url: str, log_dir, api_version="v1",
1618
extra_headers: dict | None = None, timeout: int = 180, max_retries: int = 3,
17-
use_tunnel: bool | None = None, replay_log_only: bool = False) -> None:
19+
use_tunnel: bool | None = None, replay_log_only: bool = False,
20+
run_id: UUID | str | None = None) -> None:
1821
super().__init__(auth_token, base_url, log_dir=log_dir, api_version=api_version,
1922
extra_headers=extra_headers, timeout=timeout, max_retries=max_retries,
20-
use_tunnel=use_tunnel, replay_log_only=replay_log_only)
23+
use_tunnel=use_tunnel, replay_log_only=replay_log_only, run_id=run_id)
2124

2225
def submit_generic_run(self, build_id: str, run_id: str, started_by: str, build_url: str, sub_type: str = None, scylla_version: str | None = None):
2326
request_body = {

argus/client/replay_log.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,17 @@
2121

2222
import atexit
2323
import functools
24-
import itertools
2524
import json
2625
import logging
2726
import os
2827
import re
2928
import threading
3029
import time
30+
import uuid
3131
import weakref
3232
from pathlib import Path
3333
from typing import IO
3434

35-
_instance_counter = itertools.count()
36-
3735
LOGGER = logging.getLogger(__name__)
3836

3937
# Allow only characters that are unambiguously safe inside a filename.
@@ -94,12 +92,13 @@ def __init__(
9492
run_id: str | None = None,
9593
test_type: str | None = None,
9694
) -> None:
97-
safe_run_id = _sanitize_for_filename(run_id or "unknown")
95+
safe_run_id = _sanitize_for_filename(run_id or "unknown")[:64]
9896
log_dir_path = Path(log_dir)
99-
# Nanosecond clock + pid + process-wide counter guarantees uniqueness
100-
# across parallel processes and back-to-back instantiation, even when
101-
# the system clock has coarser-than-nanosecond resolution.
102-
suffix = f"{_now_ns()}_{os.getpid()}_{next(_instance_counter)}"
97+
# A uuid4 guarantees uniqueness on its own, so collisions can't happen
98+
# even when ``log_dir`` is a mount shared across hosts/containers where
99+
# pids and clocks aren't comparable. The timestamp and pid are kept
100+
# only to make the filename sortable/debuggable, not for uniqueness.
101+
suffix = f"{_now_ns()}_{os.getpid()}_{uuid.uuid4().hex}"
103102
self._path: Path = log_dir_path / f"argus_replay_log_{safe_run_id}_{suffix}.jsonl"
104103
self._test_type: str = test_type or "unknown"
105104
self._lock = threading.Lock()

argus/client/tests/test_replay_log.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,21 @@ def test_argus_client_replay_log_path_includes_run_id_when_set(tmp_path):
522522
assert str(run_id) in client.replay_log_path.name
523523
assert "scylla-cluster-tests" not in client.replay_log_path.name # test_type isn't in filename
524524
client.close()
525+
526+
527+
def test_argus_generic_client_replay_log_path_includes_run_id_when_set(tmp_path):
528+
# Regression test: ArgusGenericClient used to drop run_id entirely, so
529+
# every replay log fell back to "unknown" regardless of what pytest-argus-
530+
# reporter (or any other caller) passed in.
531+
from argus.client.generic.client import ArgusGenericClient
532+
run_id = uuid4()
533+
client = ArgusGenericClient(
534+
run_id=run_id,
535+
auth_token="t",
536+
base_url="https://test.example.com",
537+
log_dir=tmp_path,
538+
replay_log_only=True,
539+
)
540+
assert str(run_id) in client.replay_log_path.name
541+
assert "unknown" not in client.replay_log_path.name
542+
client.close()

pytest-argus-reporter/pytest_argus_reporter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ def argus_client(self):
215215
log_dir=self.log_dir,
216216
use_tunnel=self.use_tunnel,
217217
extra_headers=self.extra_headers,
218+
run_id=self.run_id,
218219
)
219220

220221
def append_test_data(self, request, test_data):

pytest-argus-reporter/tests/test_argus_reporter.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,31 @@ def test_1(argus_reporter):
442442
assert not requests_mock.called, "Requests are not made to Argus when post_reports is False"
443443

444444

445+
def test_replay_log_filename_carries_run_id(testdir, requests_mock): # pylint: disable=redefined-outer-name
446+
# Regression test: ArgusReporter.argus_client used to construct
447+
# ArgusGenericClient without run_id, so the replay log filename always
448+
# fell back to "unknown" instead of the actual ARGUS_RUN_ID (set to
449+
# "1234" by the configure_needed_env_vars fixture).
450+
testdir.makepyfile(
451+
"""
452+
import pytest
453+
454+
def test_1(argus_reporter):
455+
argus_reporter.argus_client # force construction
456+
"""
457+
)
458+
459+
result = testdir.runpytest("--argus-post-reports", "-s", "-v")
460+
461+
result.stdout.fnmatch_lines(["*::test_1 PASSED*"])
462+
assert result.ret == 0
463+
464+
replay_logs = list(testdir.tmpdir.visit("argus_replay_log_*"))
465+
assert replay_logs, "expected a replay log file to be created"
466+
assert "1234" in replay_logs[0].basename
467+
assert "unknown" not in replay_logs[0].basename
468+
469+
445470
def test_subtests(testdir, requests_mock): # pylint: disable=redefined-outer-name
446471
"""Make sure subtests are identified and reported."""
447472

0 commit comments

Comments
 (0)