Skip to content

OSAC-1593: add console proxy e2e tests#71

Open
sk-ilya wants to merge 2 commits into
osac-project:mainfrom
sk-ilya:websocket
Open

OSAC-1593: add console proxy e2e tests#71
sk-ilya wants to merge 2 commits into
osac-project:mainfrom
sk-ilya:websocket

Conversation

@sk-ilya

@sk-ilya sk-ilya commented Jun 16, 2026

Copy link
Copy Markdown

Add 9 tests covering both WebSocket and gRPC stream transports for the serial console feature. A module-scoped fixture provisions a single VM shared across all tests.

Tests cover: bidirectional data flow, 90-second idle survival, single-use ticket enforcement (JTI), expired ticket rejection, concurrent session rejection, non-existent VM, and invalid ticket handling.

Summary by CodeRabbit

  • Tests

    • Added end-to-end serial console coverage for both WebSocket and gRPC streaming, including bidirectional I/O.
    • Validates ~90s idle tolerance, single-use ticket enforcement, concurrent-session rejection, expired-ticket behavior, and negative cases for invalid tickets and nonexistent VMs.
  • Chores / Configuration

    • Added websocket-client and pytest-xdist to runtime/test dependencies.
    • Updated pytest parallel execution to use 4 workers with load-file distribution.

@openshift-ci-robot

openshift-ci-robot commented Jun 16, 2026

Copy link
Copy Markdown

@sk-ilya: This pull request references OSAC-420 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Add 9 tests covering both WebSocket and gRPC stream transports for the serial console feature. A module-scoped fixture provisions a single VM shared across all tests.

Tests cover: bidirectional data flow, 90-second idle survival, single-use ticket enforcement (JTI), expired ticket rejection, concurrent session rejection, non-existent VM, and invalid ticket handling.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci Bot requested review from rgolangh and tzumainn June 16, 2026 16:07
@openshift-ci

openshift-ci Bot commented Jun 16, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: sk-ilya
Once this PR has been reviewed and has the lgtm label, please assign trewest for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds websocket-client>=1.6 as a project dependency and pytest-xdist parallel execution config, extends GRPCClient with a create_console_session method, and introduces a new tests/vmaas/test_console.py module with a VM fixture, five transport helper functions, and nine Pytest test cases covering WebSocket and gRPC serial console session behavior including positive flows, idle survival, ticket enforcement, and error cases.

Changes

Serial Console Session Test Suite

Layer / File(s) Summary
Dependencies and GRPCClient.create_console_session
pyproject.toml, tests/core/grpc_client.py
Adds websocket-client>=1.6 to project dependencies and enables pytest-xdist parallel execution with addopts = "-n 4 --dist loadfile". Adds GRPCClient.create_console_session that builds the ConsoleSessions/Create request payload from resource_type, resource_id, console_type, and optional client_id, then returns the created session object. Condenses ensure_organization call to single-line form.
Test fixture and transport helpers
tests/vmaas/test_console.py
Adds imports, WebSocket and gRPC proxy endpoint constants, a module-scoped console_vm fixture (provisions VM, waits until Running, yields UUID/name, performs best-effort cleanup), and helpers: _ws_url (URL builder from fulfillment address), _ws_connect (opens ticket-authenticated WebSocket with bearer token and binary subprotocol), _ws_try_connect (boolean probe), _grpc_stream (spawns grpcurl with bearer auth and optional base64-encoded stdin), _create_ticket (creates serial console session ticket).
Happy-path and idle-tolerance tests
tests/vmaas/test_console.py
Adds test_console_serial_websocket and test_console_serial_grpc_stream testing bidirectional I/O over both transports. Adds test_console_websocket_idle_90s and test_console_grpc_stream_idle_90s validating that connections survive ~90 seconds of inactivity before sending and receiving data.
Ticket enforcement and negative tests
tests/vmaas/test_console.py
Adds test_console_ticket_reuse_rejected (enforces single-use: first WebSocket connection succeeds, remaining four fail), test_console_concurrent_session_rejected (rejects second session with FailedPrecondition while first is active), test_console_expired_ticket_rejected (waits past expiresAt and asserts connection fails), test_console_session_nonexistent_vm (asserts creation for nonexistent VM returns NotFound), test_console_invalid_ticket_websocket (asserts invalid ticket raises WebSocketException).

Sequence Diagram(s)

sequenceDiagram
  participant Test as Test
  participant GRPCClient
  participant ConsoleAPI as Console API (gRPC)
  participant WSProxy as WebSocket Proxy

  rect rgba(70, 130, 180, 0.5)
    note over Test,WSProxy: Create console session and connect
    Test->>GRPCClient: create_console_session(resource_type, resource_id, console_type)
    GRPCClient->>ConsoleAPI: ConsoleSessions/Create
    ConsoleAPI-->>GRPCClient: ticket, expiresAt
    GRPCClient-->>Test: session dict
    Test->>WSProxy: _ws_connect(url, ticket)
    WSProxy-->>Test: WebSocket (binary subprotocol, initial output)
  end

  rect rgba(200, 80, 80, 0.5)
    note over Test,WSProxy: Ticket enforcement and rejection
    Test->>WSProxy: _ws_try_connect(url, same_ticket) x5
    WSProxy-->>Test: true (1st), false (2nd–5th)
    Test->>WSProxy: _ws_try_connect(url, expired_ticket)
    WSProxy-->>Test: false (WebSocketException)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🖥️ A console knocks upon the door,
With tickets, streams, and sockets galore,
One use per key, no sneaking twice,
Idle or expired? The rules aren't nice.
NotFound VM? Rejected clean—
The strictest console tests you've seen! 🔒

🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'OSAC-1593: add console proxy e2e tests' accurately summarizes the main change: adding end-to-end tests for the console proxy feature, as evidenced by the new test file and supporting changes.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No-Hardcoded-Secrets ✅ Passed No hardcoded secrets found. All tokens/credentials are passed as parameters or obtained from API responses, not embedded as literals. The only hardcoded auth-related string is "not-a-valid-ticket"...
No-Weak-Crypto ✅ Passed No weak cryptographic patterns detected. Code contains no MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB usage; no custom crypto implementations; and no insecure token/secret comparisons.
No-Injection-Vectors ✅ Passed No SQL concatenation, shell injection, eval/exec, pickle/yaml loading, or os.system patterns detected. All subprocess calls use safe list-form arguments without shell=True.
Container-Privileges ✅ Passed PR modifies only Python test code and project configuration; no container/K8s manifests with privileged settings present.
No-Sensitive-Data-In-Logs ✅ Passed No sensitive data (tokens, passwords, API keys, PII) is exposed in logging. Tokens/tickets are used in function parameters and headers but never logged. Exception logging contains only connection e...
Ai-Attribution ✅ Passed AI tool (Claude) was used and properly attributed with "Assisted-by: Claude noreply@anthropic.com" trailer in the commit message. No incorrect Co-Authored-By usage for AI.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sk-ilya

sk-ilya commented Jun 16, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sk-ilya

sk-ilya commented Jun 16, 2026

Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@sk-ilya

sk-ilya commented Jun 16, 2026

Copy link
Copy Markdown
Author

/retest-required

2 similar comments
@sk-ilya

sk-ilya commented Jun 16, 2026

Copy link
Copy Markdown
Author

/retest-required

@sk-ilya

sk-ilya commented Jun 16, 2026

Copy link
Copy Markdown
Author

/retest-required

@sk-ilya

sk-ilya commented Jun 16, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyproject.toml`:
- Line 9: Update the websocket-client dependency specification in pyproject.toml
from the floating version constraint (>=1.6) to an exact version pin (==1.9.0)
to match the version already pinned in the uv.lock file and align with
supply-chain best practices for explicit version pinning.

In `@tests/vmaas/test_console.py`:
- Around line 41-46: The test fixture setup code contains calls to
wait_for_provision and wait_for_running (lines 44-45) that can raise exceptions,
and if they do, the cleanup code at lines 50-56 is never executed, causing VM
resource leaks. Wrap the entire setup sequence from line 41 through line 48 in a
try-finally block, keeping the setup code in the try block and moving the
cleanup code to the finally block to guarantee cleanup runs regardless of
whether the setup succeeds or fails.
- Around line 264-271: The issue is that the min(60.0, ...) cap on the
wait_seconds calculation in the _ws_try_connect test can cause the test to
attempt connection before the ticket actually expires. When seconds_until_expiry
is greater than approximately 45 seconds, the 60-second cap prevents waiting the
full (expiry_time + 15_second_buffer), causing the connection attempt at line
271 to occur while the ticket is still valid rather than after it has expired.
Remove the min(60.0, ...) cap to ensure wait_seconds always equals
seconds_until_expiry + 15.0, guaranteeing the ticket has expired before the
assertion in line 271 is evaluated.
- Around line 75-83: The _ws_try_connect function contains a ws.recv() call that
waits for the first WebSocket frame, which can timeout on legitimate but silent
connections and produce false negatives. Since a successful return from
_ws_connect() already confirms the WebSocket handshake has succeeded, remove the
ws.recv() line entirely and keep only the ws.close() call before returning True.
This eliminates unnecessary waiting on frame reception and prevents false
negatives for valid ticket enforcement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: dce0293f-d520-4dba-a97c-4d00b2cb52ed

📥 Commits

Reviewing files that changed from the base of the PR and between 5c2b986 and 3b212e3.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • pyproject.toml
  • tests/core/grpc_client.py
  • tests/vmaas/test_console.py

Comment thread pyproject.toml
Comment thread tests/vmaas/test_console.py Outdated
Comment thread tests/vmaas/test_console.py
Comment thread tests/vmaas/test_console.py
@sk-ilya

sk-ilya commented Jun 17, 2026

Copy link
Copy Markdown
Author

/retest-required

Assisted-by: Claude <noreply@anthropic.com>
@sk-ilya sk-ilya changed the title OSAC-420: add console proxy e2e tests OSAC-1593: add console proxy e2e tests Jun 17, 2026
@openshift-ci-robot

openshift-ci-robot commented Jun 17, 2026

Copy link
Copy Markdown

@sk-ilya: This pull request references OSAC-1593 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Add 9 tests covering both WebSocket and gRPC stream transports for the serial console feature. A module-scoped fixture provisions a single VM shared across all tests.

Tests cover: bidirectional data flow, 90-second idle survival, single-use ticket enforcement (JTI), expired ticket rejection, concurrent session rejection, non-existent VM, and invalid ticket handling.

Summary by CodeRabbit

  • New Features

  • Added serial console session support using both WebSocket and gRPC streaming transports

  • Implemented console session ticket management, including single-use behavior and expiry handling

  • Tests

  • Added end-to-end coverage for bidirectional console communication

  • Validates idle tolerance (~90s), ticket reuse rejection, concurrent session exclusivity, and expired-ticket rejection

  • Includes negative cases for nonexistent VMs and invalid tickets

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@sk-ilya

sk-ilya commented Jun 17, 2026

Copy link
Copy Markdown
Author

/retest-required

1 similar comment
@sk-ilya

sk-ilya commented Jun 17, 2026

Copy link
Copy Markdown
Author

/retest-required

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/vmaas/test_console.py`:
- Around line 96-114: The proc.communicate(timeout=30) call can raise
subprocess.TimeoutExpired, which leaves the grpcurl process running and causes
process leaks. Wrap the proc.communicate(timeout=30) call in a try-except block
that catches subprocess.TimeoutExpired, and when caught, terminate the process
by calling proc.kill() or proc.terminate() before handling the exception
appropriately to ensure child processes are cleaned up and don't accumulate as
orphans.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: b635959a-5c2d-48bf-90d5-2b9b09a23e02

📥 Commits

Reviewing files that changed from the base of the PR and between 3b212e3 and d8aa2a1.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • pyproject.toml
  • tests/core/grpc_client.py
  • tests/vmaas/test_console.py

Comment on lines +96 to +114
proc = subprocess.Popen(
["grpcurl", "-insecure", "-H", f"Authorization: Bearer {ticket}", "-d", "@", address, CONSOLE_GRPC_SERVICE],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
time.sleep(wait)

if input_data is not None:
encoded: str = base64.b64encode(input_data).decode()
msg = json.dumps({"input": {"data": encoded}}) + "\n"
proc.stdin.write(msg.encode()) # type: ignore[union-attr]
proc.stdin.flush() # type: ignore[union-attr]
time.sleep(2)

proc.stdin.close() # type: ignore[union-attr]
proc.stdin = None # prevent communicate() from flushing closed stdin
stdout, stderr = proc.communicate(timeout=30)
return stdout.decode(), stderr.decode(), proc.returncode

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Terminate grpcurl on communicate() timeout to prevent process leaks.

Line 113 can raise subprocess.TimeoutExpired; currently that leaves the child process running. In repeated CI runs, this can accumulate orphaned grpcurl processes and destabilize the suite.

Suggested fix
 def _grpc_stream(
     address: str, ticket: str, *, wait: float = 5, input_data: bytes | None = None
 ) -> tuple[str, str, int]:
@@
-    proc.stdin.close()  # type: ignore[union-attr]
-    proc.stdin = None  # prevent communicate() from flushing closed stdin
-    stdout, stderr = proc.communicate(timeout=30)
-    return stdout.decode(), stderr.decode(), proc.returncode
+    proc.stdin.close()  # type: ignore[union-attr]
+    proc.stdin = None  # prevent communicate() from flushing closed stdin
+    try:
+        stdout, stderr = proc.communicate(timeout=30)
+    except subprocess.TimeoutExpired:
+        proc.kill()
+        stdout, stderr = proc.communicate()
+        raise
+    return stdout.decode(), stderr.decode(), proc.returncode
🧰 Tools
🪛 ast-grep (0.43.0)

[info] 105-105: use jsonify instead of json.dumps for JSON output
Context: json.dumps({"input": {"data": encoded}})
Note: Security best practice.

(use-jsonify)

🪛 Ruff (0.15.17)

[error] 96-96: subprocess call: check for execution of untrusted input

(S603)


[error] 97-97: Starting a process with a partial executable path

(S607)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/vmaas/test_console.py` around lines 96 - 114, The
proc.communicate(timeout=30) call can raise subprocess.TimeoutExpired, which
leaves the grpcurl process running and causes process leaks. Wrap the
proc.communicate(timeout=30) call in a try-except block that catches
subprocess.TimeoutExpired, and when caught, terminate the process by calling
proc.kill() or proc.terminate() before handling the exception appropriately to
ensure child processes are cleaned up and don't accumulate as orphans.

Add pytest-xdist dependency and configure addopts to run test files
in parallel. Each file gets its own worker (--dist loadfile),
preserving module/class fixture scoping while reducing wall clock
from ~63 min to ~15 min.

Assisted-by: Claude <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyproject.toml`:
- Line 19: The pytest addopts configuration in pyproject.toml hardcodes the
worker count to 4 with `-n 4`, which assumes all test environments have at least
4 CPU cores and reduces portability. Replace `-n 4` with `-n auto` in the
addopts line to dynamically detect the CPU count at runtime, making the
configuration more adaptable across different environments with varying core
counts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: df74a789-49d2-4a47-ab10-1354a1ab0a11

📥 Commits

Reviewing files that changed from the base of the PR and between d8aa2a1 and 22e2c7c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • pyproject.toml

Comment thread pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]
addopts = "-n 4 --dist loadfile"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider -n auto for better portability across environments.

Hardcoding -n 4 assumes all test environments have at least 4 cores. The -n auto option dynamically detects CPU count and is more portable. If the fixed count is intentional for your specific CI environment, consider documenting the rationale.

⚡ Suggested change for dynamic worker count
-addopts = "-n 4 --dist loadfile"
+addopts = "-n auto --dist loadfile"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
addopts = "-n 4 --dist loadfile"
addopts = "-n auto --dist loadfile"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` at line 19, The pytest addopts configuration in
pyproject.toml hardcodes the worker count to 4 with `-n 4`, which assumes all
test environments have at least 4 CPU cores and reduces portability. Replace `-n
4` with `-n auto` in the addopts line to dynamically detect the CPU count at
runtime, making the configuration more adaptable across different environments
with varying core counts.

@openshift-ci

openshift-ci Bot commented Jun 17, 2026

Copy link
Copy Markdown

@sk-ilya: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-vmaas 22e2c7c link true /test e2e-vmaas

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants