Skip to content

refactor(client/tunnel): convert tunnel_* files into tunnel package module#2

Closed
CodeLieutenant wants to merge 29 commits into
masterfrom
refactor/argus-ssh-tunnel
Closed

refactor(client/tunnel): convert tunnel_* files into tunnel package module#2
CodeLieutenant wants to merge 29 commits into
masterfrom
refactor/argus-ssh-tunnel

Conversation

@CodeLieutenant

Copy link
Copy Markdown
Owner

Summary

Addresses Petr's review comment on the SCT PR:

"This is an issue for the client, but I think we should have been using module here, instead of using tunnel_* files. The prefix itself signals that these should be grouped into a module"

The four flat tunnel_*.py files and the tunnel.py re-export facade have been reorganised into a proper subpackage:

argus/client/tunnel/
├── __init__.py   ← public re-exports (was tunnel.py)
├── models.py     ← was tunnel_models.py
├── state.py      ← was tunnel_state.py
├── api.py        ← was tunnel_api.py
└── ssh.py        ← was tunnel_ssh.py

This is not a breaking change

All public import paths are identical before and after this refactor.

Python resolves tunnel/__init__.py and tunnel.py identically, so every existing import continues to work without modification:

from argus.client.tunnel import SSHTunnel, TunnelConfig, TunnelClientError
from argus.client.tunnel import resolve_tunnel_config, resolve_tunnel_config_with_reason
from argus.client.tunnel import delete_cached_tunnel_state

Clients only need a version bump — no code changes required.

The only paths that no longer exist are the four internal implementation modules (argus.client.tunnel_api, argus.client.tunnel_state, etc.), which were never exported from argus/client/__init__.py and were not part of any public API.

k0machi and others added 29 commits May 12, 2026 16:08
This commit implements the argus run results command, replacing the stub
with a full implmentation that tries to mimic frontent response as much
as possible - Output is provided as multiple tables, each table uses
ANSI escape sequences to display the result status.
This commit adds a new subcommand and two new commands under it for the
Go cli client to be able to both list and submit new issues.
…se tag resolution

grep -v exits 1 when no lines match; with bash pipefail enabled this killed
the Resolve previous CLI tag step on the first release. Fix by wrapping grep
in a subshell with || true. Also extract all inline bash blocks into versioned
scripts under cli/scripts/ for easier local testing.
GORELEASER_CURRENT_TAG is set to the stripped semver (v0.1.1) but the
actual git tag is cli/v0.1.1, so goreleaser's validation always fails.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…ng a bare semver tag

Add monorepo.tag_prefix to .goreleaser.yml so GoReleaser strips "cli/"
for version templating while targeting the existing git tag.  Pass the
full GITHUB_REF_NAME (e.g. cli/v1.2.0) as GORELEASER_CURRENT_TAG and
remove the now-redundant extract-semver step.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Remove the monorepo.tag_prefix block (GoReleaser Pro feature) that caused
an unmarshal error on the OSS binary. Instead, the workflow strips the cli/v
prefix into a bare VERSION env var (e.g. 1.2.3) and keeps
GORELEASER_CURRENT_TAG as the full cli/vX.Y.Z tag so the GitHub Release is
still published under the prefixed tag. All .goreleaser.yml templates that
previously used {{ .Version }} now use {{ .Env.VERSION }}.
This commit adds a migration script for SCTTestRun.events field,
extracting plaintext events and processing them into fully parsed
SCTEvent table events, as well as putting any ERROR and CRITICAL events
found into SCTUnprocessedEvent event queue.

Additionally, this commit includes a script to drop events,
allocated_resources and nemesis_data columns from the primary SCTTestRun
table, to be executed after confirming the migration.

Fixes ARGUS-55
Add docs/cli-install.md with a step-by-step flow for LLM agents:
1. Check installed vs latest version (skip if up to date)
2. Install/upgrade from GitHub releases
3. Verify

Add 'Argus CLI' section to AGENTS.md pointing agents to fetch
the install guide via curl before CLI-dependent tasks.
Since we are dropping those columns, disable the old submit events
endpoint by making it a no-op.
Add project-specific rules to AGENTS.md and a /review-pr slash command
to reduce false positives in AI-generated code reviews. Rules derived
from analysis of PRs scylladb#916, scylladb#919, scylladb#926, scylladb#931, scylladb#936, scylladb#939 where 70-86%
of AI findings were noise.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eview

Move all SSH tunnel logic out of ArgusClient into a new TunneledSession
class (argus/client/session.py) that implements the requests.Session
interface. TunneledSession intercepts get/post calls, rewrites URLs from
the original base URL to the local tunnel port transparently, and manages
the monitor thread and reconnection internally.

ArgusClient.get() and post() are now trivial delegation calls with no
_ensure_tunnel() guards or tunnel-related state.

Address all @soyacz and @k0machi review comments:
- Remove --use-tunnel flags from driver_matrix_tests CLI (one-shot
  commands gain nothing from tunnel setup overhead)
- Monitor thread lives only inside TunneledSession (never created when
  tunnel is not in use)
- Simplify parse_datetime: drop Z->+00:00 workaround (Python 3.12+
  handles Z suffix natively in fromisoformat)
- Remove to_iso_z: use datetime.isoformat() directly at call sites
- Add _TunnelApiResponse TypedDict; derive required field validation
  from __required_keys__ instead of a hardcoded tuple
- Replace manual retry loop in _call_tunnel_api with a session carrying
  a Retry adapter; thread session parameter through the call chain so
  TunneledSession reuses its own _session for tunnel setup calls
- Resolve absolute ssh binary path via shutil.which and pass it into
  _build_ssh_command instead of relying on bare "ssh" on PATH
- Subclass requests.Session in TunneledSession so put/request/hooks/cookies
  all flow through the tunnel transparently; consolidate get/post into a
  single overridden request().
- Reject non-dict tunnel API payloads explicitly in _call_tunnel_api so
  unexpected list/scalar responses no longer escape as AttributeError.
- Stop wiping cached keypair on every transient establish failure; the
  cache TTL plus force_refresh handles invalidation without re-registering
  on each 30s cooldown.
- Make ArgusClient a context manager and ensure CLI commands use `with` so
  the monitor thread shuts down deterministically; weak atexit fallback
  covers callers that forget to close().
- Accept the post-985 full known_hosts entry format alongside the legacy
  SHA256 fingerprint, normalising the host token to match the connection
  target (and [host]:port for non-default ports). Strict host verification
  remains enforced — unrecognised formats raise instead of falling back.
- Split _TunnelApiResponse and _TunnelCachePayload TypedDicts; add comment
  explaining __required_keys__ usage.
- Public key chmod 0o644 (conventional) instead of 0o600.
- Tests cover retry-then-backoff, monitor lifecycle, context manager, cache
  preservation on failure, non-dict payload rejection, known_hosts entry
  acceptance, and unknown-format rejection.
- Restore original get/post formatting in base.py (revert unrelated changes).
- Add --use-tunnel option to driver_matrix CLI commands.
… compatibility

Replace all pathlib.Path usage in the SSH tunnel subsystem with plain
os.path, os, and built-in open() calls. Python 3.14 removed internal
Path attributes (_str, _drv) that caused AttributeError at runtime.
TunnelStatePaths fields are now str instead of Path throughout.
…cit tuple

Python 3.14 changed TypedDict internals so that __required_keys__ is no
longer a plain frozenset, causing a 'frozen collection __getitem__' error
at module load time. Replace the runtime introspection with a hardcoded
tuple of required field names, which is clearer and has no compatibility risk.
…nnel()

_ensure_tunnel() passed session=self to resolve_tunnel_config_with_reason(),
which called session.post() on the same TunneledSession — triggering
_ensure_tunnel() again and causing a RecursionError.

Pass session=None instead so the tunnel API uses a plain requests.Session
that bypasses the tunnel hook. The tunnel API calls target the direct Argus
URL and should never route through the tunnel themselves.
…establishment

When the SSH tunnel is active, inject headers into all requests routed
through it:
- User-Agent: argus-client-ssh-tunnel
- X-SSH-Tunnel-Origin: <proxy_host>
- X-Tunnel-Established-At: <ISO timestamp>
- X-Forwarded-Key-ID: <key_id> (when available)

Also log at INFO level when tunnel is successfully established, including
proxy host, port, user, key_id, and local port.
Signed-off-by: Dusan Malusev <dusan@dusanmalusev.dev>
This PR adds both a backend API endpoint and a Go Client command to
fetch test results globally using the provided user filters, same as
frontend implementation for the Pytest View Widget.
This commit fixes an issue where IssueBadge.svelte would not receive
owner and repo fields for github issues from SctSimilarEvents table.

Fixes ARGUS-143
This commit adds new metrics for: user-agent, ip origin and ssh tunnel
usage.

Fixes ARGUS-136
Add a copy-to-clipboard button next to the scm_revision_id field in
the TestRunInfo System Information panel. The button renders only when
scm_revision_id is non-null and navigator.clipboard is available
(HTTPS context). On click, copies the raw SHA string and shows a
success toast: "SCM revision `<sha>` copied to clipboard".

Fixes ARGUS-32
The tunnel API bootstrap calls (POST/GET /client/ssh/tunnel) were creating
a fresh requests.Session without Cloudflare Access headers (CF-Access-Client-Id,
CF-Access-Client-Secret). When argus.scylladb.com is behind Cloudflare Access,
these unauthenticated requests receive an HTML challenge page (HTTP 200) instead
of JSON, causing a misleading 'response is not JSON' error.

Fix: thread extra_headers (sourced from the parent session's headers, which
include ARGUS_EXTRA_HEADERS) through the tunnel API call chain. Also improve
the error message to include Content-Type and a body preview for easier
diagnosis of similar issues.
The public /api/v1/users endpoint was missing the email field,
requiring callers to use the admin-only endpoint just to get
user emails.
@github-actions github-actions Bot added the ai-assisted AI-assisted contribution label Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted AI-assisted contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants