Skip to content

test(maas): add ephemeral API key cleanup tests#1324

Merged
dbasunag merged 3 commits intoopendatahub-io:mainfrom
SB159:feat/maas-ephemeral-key-cleanup-tests
Mar 31, 2026
Merged

test(maas): add ephemeral API key cleanup tests#1324
dbasunag merged 3 commits intoopendatahub-io:mainfrom
SB159:feat/maas-ephemeral-key-cleanup-tests

Conversation

@SB159
Copy link
Copy Markdown
Contributor

@SB159 SB159 commented Mar 31, 2026

Pull Request

Summary

add ephemeral API key cleanup tests

Related Issues

  • Fixes:
  • JIRA:

Please review and indicate how it has been tested

  • Locally
  • Jenkins

Additional Requirements

  • If this PR introduces a new test image, did you create a PR to mirror it in disconnected environment?
  • If this PR introduces new marker(s)/adds a new component, was relevant ticket created to update relevant Jenkins job?

Summary by CodeRabbit

  • Tests
    • Added end-to-end tests validating ephemeral API key lifecycle, the automated cleanup CronJob and NetworkPolicy configuration, execution of the cleanup endpoint, and that active keys are preserved.
  • Test Utilities
    • Added fixtures to locate the API pod and create ephemeral API keys; extended API-key helper to support ephemeral keys and added subscription-info schema validation.

@SB159 SB159 requested a review from dbasunag March 31, 2026 01:50
@github-actions
Copy link
Copy Markdown

The following are automatically added/executed:

  • PR size label.
  • Run pre-commit
  • Run tox
  • Add PR author as the PR assignee
  • Build image based on the PR

Available user actions:

  • To mark a PR as WIP, add /wip in a comment. To remove it from the PR comment /wip cancel to the PR.
  • To block merging of a PR, add /hold in a comment. To un-block merging of PR comment /hold cancel.
  • To mark a PR as approved, add /lgtm in a comment. To remove, add /lgtm cancel.
    lgtm label removed on each new commit push.
  • To mark PR as verified comment /verified to the PR, to un-verify comment /verified cancel to the PR.
    verified label removed on each new commit push.
  • To Cherry-pick a merged PR /cherry-pick <target_branch_name> to the PR. If <target_branch_name> is valid,
    and the current PR is merged, a cherry-picked PR would be created and linked to the current PR.
  • To build and push image to quay, add /build-push-pr-image in a comment. This would create an image with tag
    pr-<pr_number> to quay repository. This image tag, however would be deleted on PR merge or close action.
Supported labels

{'/lgtm', '/verified', '/cherry-pick', '/wip', '/hold', '/build-push-pr-image'}

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds pytest fixtures and tests for ephemeral MaaS API key lifecycle: pod discovery, ephemeral API key creation/revocation, CronJob and NetworkPolicy assertions, and an integration test that triggers the cleanup endpoint and verifies ephemeral key visibility and persistence. Note: port‑forwarding to an internal cleanup endpoint raises an access‑control concern (CWE‑200).

Changes

Cohort / File(s) Summary
Test Fixtures
tests/model_serving/maas_billing/maas_subscription/conftest.py
Imported Kubernetes Pod and helpers; added maas_api_pod_name fixture to select a running MaaS API pod by label; added ephemeral_api_key fixture to create an ephemeral API key (ephemeral=True, expires_in="1h"), assert response contains "key" and "id", and revoke the key in teardown (accepts HTTP 200 or 404).
Ephemeral Cleanup Tests
tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py
New test module TestEphemeralKeyCleanup adding Tier1 tests that assert CronJob maas-api-key-cleanup exists and is configured (*/15 * * * *, concurrencyPolicy: Forbid, container command contains /internal/v1/api-keys/cleanup, container securityContext has runAsNonRoot=True and readOnlyRootFilesystem=True); assert NetworkPolicy maas-api-cleanup-restrict selects app: maas-api-cleanup, includes Ingress and Egress, denies ingress, and has ≥1 egress; create ephemeral key and validate visibility only when filters.includeEphemeral=True; port‑forward to the MaaS API pod to POST /internal/v1/api-keys/cleanup and assert HTTP 200 with non‑negative deletedCount; verify key remains active.
Utility Extensions
tests/model_serving/maas_billing/maas_subscription/utils.py
Extended create_api_key(...) with new parameter ephemeral: bool = False that sets payload["ephemeral"] = True when true; added assert_subscription_info_schema(sub: dict[str, Any]) to validate SubscriptionInfo-like fields and types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description follows the template structure but the 'Summary' section lacks substantive detail about what the tests validate and why they are needed. Expand the Summary section to explain what ephemeral key cleanup functionality is being tested and link to related JIRA/GitHub issues if they exist.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'test(maas): add ephemeral API key cleanup tests' directly and clearly describes the main change—adding tests for ephemeral API key cleanup functionality.

✏️ 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/model_serving/maas_billing/maas_subscription/conftest.py`:
- Around line 831-836: The teardown currently calls revoke_api_key(...) and
discards its result, which can hide failures and leak API keys; update the
ephemeral_api_key teardown to check the revoke_api_key result (and/or catch
exceptions) and fail the test or log and retry on non-successful responses: call
revoke_api_key(request_session_http=request_session_http, base_url=base_url,
key_id=create_body["id"], ocp_user_token=ocp_token_for_actor), verify the
response status or returned error value, raise an exception or assert when
revocation fails, and optionally add a small retry/backoff before raising to
handle transient failures so revoke_api_key failures are not silently ignored.
- Around line 801-809: The fixture currently ignores HTTP errors from
revoke_api_key and may pick a non-ready pod; update the teardown to capture the
response from revoke_api_key and assert/raise on non-2xx status (e.g., check
response.status_code and log/raise if not OK) so revocation failures fail the
test, and change the Pod.get selection logic (where Pod.get is called with
client=admin_client, namespace=applications_namespace,
label_selector=label_selector) to filter returned pods by readiness/Running
conditions (inspect each pod's status/conditions to require Ready=True and
phase==Running) and return a deterministically ready pod (or fail the fixture if
none match) instead of returning pods[0].

In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py`:
- Around line 80-89: The test currently only asserts policyTypes and ingress but
never validates egress rules; update the assertions around spec (the spec
variable in this test) to verify spec.egress is present and strictly restricts
destinations to the maas-api pods (e.g., an egress list with a single rule whose
to[*].podSelector.matchLabels contains app=maas-api) and does not include any
to[*].ipBlock or 0.0.0.0/0 entries; add assertions that spec.egress is a
non-empty list, that each egress rule's to entries exist, that none of those to
entries contain ipBlock or cidr ranges, and that the podSelector labels match
app=maas-api to ensure only maas-api egress is allowed.
🪄 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 YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 0b044733-caa2-405c-9d25-f163e76a3f1e

📥 Commits

Reviewing files that changed from the base of the PR and between e2d7b09 and 7468681.

📒 Files selected for processing (3)
  • tests/model_serving/maas_billing/maas_subscription/conftest.py
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py
  • tests/model_serving/maas_billing/maas_subscription/utils.py

@SB159 SB159 force-pushed the feat/maas-ephemeral-key-cleanup-tests branch from 7468681 to f088102 Compare March 31, 2026 13:02
@SB159 SB159 force-pushed the feat/maas-ephemeral-key-cleanup-tests branch from be31100 to b12eb1f Compare March 31, 2026 13:19
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py (1)

89-92: ⚠️ Potential issue | 🟠 Major

Egress rule content validation incomplete (CWE-284)

Docstring claims "restricts cleanup pod egress to maas-api only" but test only verifies egress rules exist (line 90), not that they restrict to maas-api pods. A NetworkPolicy with egress: [{}] (allow-all) would pass this test.

         egress_rules = getattr(spec, "egress", None)
         assert egress_rules, "NetworkPolicy should define at least one egress rule"
+        egress_peers = [
+            peer for rule in egress_rules for peer in (getattr(rule, "to", None) or [])
+        ]
+        assert any(
+            getattr(getattr(peer, "podSelector", None), "matchLabels", {}).get("app") == "maas-api"
+            for peer in egress_peers
+        ), "Egress must target maas-api pods"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py`
around lines 89 - 92, The test currently only checks that egress_rules exists
but not that it restricts traffic to maas-api; update the assertion to validate
content by inspecting spec.egress (egress_rules) and asserting at least one
egress rule contains a "to" entry with a podSelector (or
namespaceSelector+podSelector) that matches the maas-api selector (e.g.,
podSelector.matchLabels {'app': 'maas-api'} or the exact label used by the
maas-api Deployment) and that there are no overly permissive "to: {}" wildcards
that would allow all egress; modify the check around the egress_rules variable
(and adjust LOGGER message if desired) so the test fails unless an explicit
maas-api-targeting egress rule exists.
🧹 Nitpick comments (1)
tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py (1)

173-184: Add error handling for port-forward and JSON parsing failures

If portforward.forward() fails to establish connection within waiting=20, or if the cleanup endpoint returns non-JSON (e.g., 502 gateway error), the test will raise an uncaught exception with unclear error messaging.

Suggested improvement
         with portforward.forward(
             pod_or_service=maas_api_pod_name,
             namespace=applications_namespace,
             from_port=8080,
             to_port=8080,
             waiting=20,
         ):
             cleanup_response = requests.post(
                 url="http://localhost:8080/internal/v1/api-keys/cleanup",
                 timeout=30,
             )
 
         assert cleanup_response.status_code == 200, (
             f"Cleanup endpoint returned unexpected status: {cleanup_response.status_code}: "
             f"{(cleanup_response.text or '')[:200]}"
         )
-        cleanup_resp = cleanup_response.json()
+        try:
+            cleanup_resp = cleanup_response.json()
+        except requests.exceptions.JSONDecodeError as e:
+            pytest.fail(f"Cleanup response not valid JSON: {cleanup_response.text[:200]}, error: {e}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py`
around lines 173 - 184, Wrap the portforward.forward() call and the HTTP
call/JSON parsing around the cleanup endpoint in try/except blocks so failures
give clear test failures instead of uncaught exceptions: catch exceptions from
portforward.forward() (the call that uses waiting=20) and raise an assertion or
pytest.fail with a message that includes the original exception; likewise catch
requests.exceptions.RequestException around the requests.post to
"http://localhost:8080/internal/v1/api-keys/cleanup" and fail with the status
code and response text if available, and catch ValueError/JSONDecodeError when
calling cleanup_response.json() to fail with the raw response.text (or status)
so non-JSON responses (e.g., 502) produce clear diagnostics. Ensure you
reference the existing variables cleanup_response and cleanup_resp in the error
messages so the test output points to the failing endpoint and payload.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py`:
- Around line 89-92: The test currently only checks that egress_rules exists but
not that it restricts traffic to maas-api; update the assertion to validate
content by inspecting spec.egress (egress_rules) and asserting at least one
egress rule contains a "to" entry with a podSelector (or
namespaceSelector+podSelector) that matches the maas-api selector (e.g.,
podSelector.matchLabels {'app': 'maas-api'} or the exact label used by the
maas-api Deployment) and that there are no overly permissive "to: {}" wildcards
that would allow all egress; modify the check around the egress_rules variable
(and adjust LOGGER message if desired) so the test fails unless an explicit
maas-api-targeting egress rule exists.

---

Nitpick comments:
In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py`:
- Around line 173-184: Wrap the portforward.forward() call and the HTTP
call/JSON parsing around the cleanup endpoint in try/except blocks so failures
give clear test failures instead of uncaught exceptions: catch exceptions from
portforward.forward() (the call that uses waiting=20) and raise an assertion or
pytest.fail with a message that includes the original exception; likewise catch
requests.exceptions.RequestException around the requests.post to
"http://localhost:8080/internal/v1/api-keys/cleanup" and fail with the status
code and response text if available, and catch ValueError/JSONDecodeError when
calling cleanup_response.json() to fail with the raw response.text (or status)
so non-JSON responses (e.g., 502) produce clear diagnostics. Ensure you
reference the existing variables cleanup_response and cleanup_resp in the error
messages so the test output points to the failing endpoint and payload.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 04475368-813c-4687-ac76-7280b6e96d46

📥 Commits

Reviewing files that changed from the base of the PR and between be31100 and b12eb1f.

📒 Files selected for processing (3)
  • tests/model_serving/maas_billing/maas_subscription/conftest.py
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py
  • tests/model_serving/maas_billing/maas_subscription/utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/model_serving/maas_billing/maas_subscription/utils.py

@SB159 SB159 force-pushed the feat/maas-ephemeral-key-cleanup-tests branch 2 times, most recently from bad324f to 9d24737 Compare March 31, 2026 13:33
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/model_serving/maas_billing/maas_subscription/utils.py (1)

191-219: ⚠️ Potential issue | 🟠 Major

Enforce expiration when ephemeral=True (CWE-613, major).

Exploit scenario: a caller sets ephemeral=True without expires_in; if backend accepts it, this creates a hidden credential that may persist indefinitely and evade cleanup expectations.

Remediation diff
 def create_api_key(
@@
     if subscription is not None:
         payload["subscription"] = subscription
+    if ephemeral and not expires_in:
+        raise AssertionError("ephemeral API keys must set expires_in")
     if ephemeral:
         payload["ephemeral"] = True

As per coding guidelines, "REVIEW PRIORITIES: 1. Security vulnerabilities (provide severity, exploit scenario, and remediation code)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/model_serving/maas_billing/maas_subscription/utils.py` around lines 191
- 219, The function builds an API key payload but allows ephemeral=True without
an expiration, which can create persistent hidden credentials; update the
function that accepts expires_in and ephemeral (referencing the expires_in and
ephemeral parameters and the payload dict) to enforce that when ephemeral is
True expires_in must be provided (e.g., raise ValueError or AssertionError
early), and ensure you do not send an ephemeral key without
payload["expiresIn"]; include a clear error message mentioning ephemeral and
expires_in so callers and tests fail fast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/model_serving/maas_billing/maas_subscription/utils.py`:
- Around line 492-494: The loop over sub["model_refs"] assumes each ref is a
mapping; add an explicit type guard before key assertions by asserting
isinstance(ref, dict) with a clear message (e.g., "model_ref must be a dict:
{ref}"), then proceed to assert "name" in ref and isinstance(ref["name"], str);
update the checks around the for loop in the same scope where sub and model_refs
are validated (the for ref in sub["model_refs"] block) so malformed API
responses fail with a clear, descriptive assertion instead of a TypeError.
- Around line 484-503: The assertions currently interpolate the entire sub
payload (f"{sub}") which can leak sensitive tenant data; update each assertion
in the validation block to avoid dumping the full subscription object by
removing f"{sub}" from messages and instead use a generic, non-sensitive message
(e.g., "Missing subscription_id_header" or "subscription_id_header must be a
string") or include only a non-sensitive identifier (like the key name). Locate
the checks referencing sub and ref (the assert lines for
"subscription_id_header", "subscription_description", "priority", "model_refs",
model_ref "name", and optional keys "display_name", "organization_id",
"cost_center", "labels") and replace their f-string payloads with these generic
messages so no full payload or tenant identifiers are logged on assertion
failure.

---

Outside diff comments:
In `@tests/model_serving/maas_billing/maas_subscription/utils.py`:
- Around line 191-219: The function builds an API key payload but allows
ephemeral=True without an expiration, which can create persistent hidden
credentials; update the function that accepts expires_in and ephemeral
(referencing the expires_in and ephemeral parameters and the payload dict) to
enforce that when ephemeral is True expires_in must be provided (e.g., raise
ValueError or AssertionError early), and ensure you do not send an ephemeral key
without payload["expiresIn"]; include a clear error message mentioning ephemeral
and expires_in so callers and tests fail fast.
🪄 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 YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 74defc68-a416-4b62-af3b-67fbbff25836

📥 Commits

Reviewing files that changed from the base of the PR and between b12eb1f and 9d24737.

📒 Files selected for processing (3)
  • tests/model_serving/maas_billing/maas_subscription/conftest.py
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py
  • tests/model_serving/maas_billing/maas_subscription/utils.py
✅ Files skipped from review due to trivial changes (1)
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/model_serving/maas_billing/maas_subscription/conftest.py

Signed-off-by: Swati Mukund Bagal <sbagal@redhat.com>
@SB159 SB159 force-pushed the feat/maas-ephemeral-key-cleanup-tests branch from 9d24737 to 57c44ee Compare March 31, 2026 13:44
Signed-off-by: Swati Mukund Bagal <sbagal@redhat.com>
@SB159 SB159 force-pushed the feat/maas-ephemeral-key-cleanup-tests branch from 828bab8 to 312e7d8 Compare March 31, 2026 16:19
@SB159 SB159 requested a review from dbasunag March 31, 2026 16:21
@sheltoncyril
Copy link
Copy Markdown
Contributor

/lgtm

@dbasunag dbasunag merged commit 8968196 into opendatahub-io:main Mar 31, 2026
9 checks passed
@github-actions
Copy link
Copy Markdown

Status of building tag latest: success.
Status of pushing tag latest to image registry: success.

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.

4 participants