Skip to content

Commit 312e7d8

Browse files
committed
fix: address review comments
Signed-off-by: Swati Mukund Bagal <sbagal@redhat.com>
1 parent 57c44ee commit 312e7d8

File tree

3 files changed

+105
-77
lines changed

3 files changed

+105
-77
lines changed

tests/model_serving/maas_billing/maas_subscription/conftest.py

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import requests
66
import structlog
77
from kubernetes.dynamic import DynamicClient
8+
from ocp_resources.cron_job import CronJob
89
from ocp_resources.llm_inference_service import LLMInferenceService
910
from ocp_resources.maas_auth_policy import MaaSAuthPolicy
1011
from ocp_resources.maas_model_ref import MaaSModelRef
1112
from ocp_resources.maas_subscription import MaaSSubscription
1213
from ocp_resources.namespace import Namespace
14+
from ocp_resources.network_policy import NetworkPolicy
1315
from ocp_resources.pod import Pod
1416
from ocp_resources.resource import ResourceEditor
1517
from ocp_resources.service_account import ServiceAccount
@@ -791,23 +793,55 @@ def short_expiration_api_key_id(
791793
)
792794

793795

796+
@pytest.fixture()
797+
def maas_cleanup_cronjob(
798+
admin_client: DynamicClient,
799+
) -> CronJob:
800+
"""Return the maas-api-key-cleanup CronJob, asserting it exists."""
801+
applications_namespace = py_config["applications_namespace"]
802+
cronjob = CronJob(
803+
client=admin_client,
804+
name="maas-api-key-cleanup",
805+
namespace=applications_namespace,
806+
)
807+
assert cronjob.exists, f"CronJob maas-api-key-cleanup not found in {applications_namespace}"
808+
return cronjob
809+
810+
811+
@pytest.fixture()
812+
def maas_cleanup_networkpolicy(
813+
admin_client: DynamicClient,
814+
) -> NetworkPolicy:
815+
"""Return the maas-api-cleanup-restrict NetworkPolicy, asserting it exists."""
816+
applications_namespace = py_config["applications_namespace"]
817+
network_policy = NetworkPolicy(
818+
client=admin_client,
819+
name="maas-api-cleanup-restrict",
820+
namespace=applications_namespace,
821+
)
822+
assert network_policy.exists, f"NetworkPolicy maas-api-cleanup-restrict not found in {applications_namespace}"
823+
return network_policy
824+
825+
794826
@pytest.fixture()
795827
def maas_api_pod_name(
796828
admin_client: DynamicClient,
797829
) -> str:
798-
"""Return the name of the first running maas-api pod."""
830+
"""Return the name of the single running maas-api pod (exactly one pod is expected)."""
799831
applications_namespace = py_config["applications_namespace"]
800832
label_selector = ",".join(f"{k}={v}" for k, v in get_maas_api_labels().items())
801-
all_pods = list(
833+
pods = list(
802834
Pod.get(
803835
client=admin_client,
804836
namespace=applications_namespace,
805837
label_selector=label_selector,
806838
)
807839
)
808-
running_pods = [pod for pod in all_pods if pod.instance.status.phase == "Running"]
809-
assert running_pods, f"No Running maas-api pods found in {applications_namespace}"
810-
return running_pods[0].name
840+
assert len(pods) == 1, f"Expected exactly 1 maas-api pod in {applications_namespace}, found {len(pods)}"
841+
assert pods[0].instance.status.phase == "Running", (
842+
f"maas-api pod '{pods[0].name}' is not Running (phase={pods[0].instance.status.phase})"
843+
)
844+
return pods[0].name
811845

812846

813847
@pytest.fixture()
@@ -817,7 +851,7 @@ def ephemeral_api_key(
817851
ocp_token_for_actor: str,
818852
) -> Generator[dict[str, Any]]:
819853
"""Create an ephemeral API key and revoke it on teardown."""
820-
create_resp, create_body = create_api_key(
854+
creation_response, api_key_data = create_api_key(
821855
base_url=base_url,
822856
ocp_user_token=ocp_token_for_actor,
823857
request_session_http=request_session_http,
@@ -826,16 +860,18 @@ def ephemeral_api_key(
826860
ephemeral=True,
827861
raise_on_error=False,
828862
)
829-
assert_api_key_created_ok(resp=create_resp, body=create_body, required_fields=("key", "id"))
830-
LOGGER.info(f"[ephemeral] Created ephemeral key: id={create_body['id']}, expiresAt={create_body.get('expiresAt')}")
831-
yield create_body
832-
revoke_resp, _ = revoke_api_key(
863+
assert_api_key_created_ok(resp=creation_response, body=api_key_data, required_fields=("key", "id"))
864+
LOGGER.info(
865+
f"[ephemeral] Created ephemeral key: id={api_key_data['id']}, expiresAt={api_key_data.get('expiresAt')}"
866+
)
867+
yield api_key_data
868+
revoke_response, _ = revoke_api_key(
833869
request_session_http=request_session_http,
834870
base_url=base_url,
835-
key_id=create_body["id"],
871+
key_id=api_key_data["id"],
836872
ocp_user_token=ocp_token_for_actor,
837873
)
838-
if revoke_resp.status_code not in (200, 404):
874+
if revoke_response.status_code not in (200, 404):
839875
raise AssertionError(
840-
f"Unexpected teardown status for ephemeral key id={create_body['id']}: {revoke_resp.status_code}"
876+
f"Unexpected teardown status for ephemeral key id={api_key_data['id']}: {revoke_response.status_code}"
841877
)

tests/model_serving/maas_billing/maas_subscription/test_api_key_ephemeral_cleanup.py

Lines changed: 33 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
from __future__ import annotations
2-
31
from typing import Any
42

53
import portforward
64
import pytest
75
import requests
86
import structlog
9-
from kubernetes.dynamic import DynamicClient
107
from ocp_resources.cron_job import CronJob
118
from ocp_resources.network_policy import NetworkPolicy
129
from pytest_testconfig import config as py_config
1310

11+
from tests.model_serving.maas_billing.maas_subscription.utils import search_active_api_keys
1412
from tests.model_serving.maas_billing.utils import build_maas_headers
1513

1614
LOGGER = structlog.get_logger(name=__name__)
1715

18-
MAAS_CLEANUP_CRONJOB_NAME = "maas-api-key-cleanup"
19-
MAAS_CLEANUP_NETWORKPOLICY_NAME = "maas-api-cleanup-restrict"
20-
2116

2217
@pytest.mark.usefixtures(
2318
"maas_subscription_controller_enabled_latest",
@@ -28,18 +23,9 @@ class TestEphemeralKeyCleanup:
2823
"""Tests for ephemeral API key cleanup (CronJob + internal endpoint)."""
2924

3025
@pytest.mark.tier1
31-
def test_cronjob_exists_and_configured(self, admin_client: DynamicClient) -> None:
26+
def test_cronjob_exists_and_configured(self, maas_cleanup_cronjob: CronJob) -> None:
3227
"""Verify the maas-api-key-cleanup CronJob exists with expected configuration."""
33-
applications_namespace = py_config["applications_namespace"]
34-
35-
cronjob = CronJob(
36-
client=admin_client,
37-
name=MAAS_CLEANUP_CRONJOB_NAME,
38-
namespace=applications_namespace,
39-
)
40-
assert cronjob.exists, f"CronJob {MAAS_CLEANUP_CRONJOB_NAME} not found in {applications_namespace}"
41-
42-
spec = cronjob.instance.spec
28+
spec = maas_cleanup_cronjob.instance.spec
4329

4430
assert spec.schedule == "*/15 * * * *", f"Expected schedule '*/15 * * * *', got '{spec.schedule}'"
4531
assert spec.concurrencyPolicy == "Forbid", (
@@ -62,26 +48,15 @@ def test_cronjob_exists_and_configured(self, admin_client: DynamicClient) -> Non
6248
LOGGER.info(f"[ephemeral] CronJob validated: schedule={spec.schedule}, concurrency={spec.concurrencyPolicy}")
6349

6450
@pytest.mark.tier1
65-
def test_cleanup_networkpolicy_exists(self, admin_client: DynamicClient) -> None:
51+
def test_cleanup_networkpolicy_exists(self, maas_cleanup_networkpolicy: NetworkPolicy) -> None:
6652
"""Verify the cleanup NetworkPolicy restricts cleanup pod egress to maas-api only."""
67-
applications_namespace = py_config["applications_namespace"]
68-
69-
network_policy = NetworkPolicy(
70-
client=admin_client,
71-
name=MAAS_CLEANUP_NETWORKPOLICY_NAME,
72-
namespace=applications_namespace,
73-
)
74-
assert network_policy.exists, (
75-
f"NetworkPolicy {MAAS_CLEANUP_NETWORKPOLICY_NAME} not found in {applications_namespace}"
76-
)
77-
78-
spec = network_policy.instance.spec
53+
spec = maas_cleanup_networkpolicy.instance.spec
7954

8055
assert spec.podSelector.matchLabels.get("app") == "maas-api-cleanup", (
8156
f"NetworkPolicy should target app=maas-api-cleanup pods, got: {spec.podSelector.matchLabels}"
8257
)
83-
assert "Egress" in spec.policyTypes, "NetworkPolicy should control Egress traffic"
84-
assert "Ingress" in spec.policyTypes, "NetworkPolicy should control Ingress traffic"
58+
for policy_type in ("Egress", "Ingress"):
59+
assert policy_type in spec.policyTypes, f"NetworkPolicy should control {policy_type} traffic"
8560

8661
ingress_rules = getattr(spec, "ingress", None)
8762
assert ingress_rules in ([], None), "Cleanup pods should have no inbound traffic allowed"
@@ -93,57 +68,51 @@ def test_cleanup_networkpolicy_exists(self, admin_client: DynamicClient) -> None
9368

9469
@pytest.mark.tier1
9570
@pytest.mark.parametrize("ocp_token_for_actor", [{"type": "free"}], indirect=True)
96-
def test_create_ephemeral_key(
71+
def test_ephemeral_key_visible_with_include_filter(
9772
self,
9873
request_session_http: requests.Session,
9974
base_url: str,
10075
ocp_token_for_actor: str,
10176
ephemeral_api_key: dict[str, Any],
10277
) -> None:
103-
"""Verify ephemeral keys are visible with includeEphemeral filter but hidden by default."""
78+
"""Verify ephemeral key is marked as ephemeral and visible when includeEphemeral=True."""
10479
key_id = ephemeral_api_key["id"]
105-
api_keys_endpoint = f"{base_url}/v1/api-keys"
106-
auth_header = build_maas_headers(token=ocp_token_for_actor)
10780

10881
assert ephemeral_api_key.get("ephemeral") is True, "Key should be marked as ephemeral"
10982

110-
r_search = request_session_http.post(
111-
url=f"{api_keys_endpoint}/search",
112-
headers=auth_header,
113-
json={
114-
"filters": {"status": ["active"], "includeEphemeral": True},
115-
"pagination": {"limit": 50, "offset": 0},
116-
},
117-
timeout=30,
118-
)
119-
assert r_search.status_code == 200, (
120-
f"Expected 200 from search with includeEphemeral=True, "
121-
f"got {r_search.status_code}: {(r_search.text or '')[:200]}"
83+
items = search_active_api_keys(
84+
request_session_http=request_session_http,
85+
base_url=base_url,
86+
ocp_user_token=ocp_token_for_actor,
87+
include_ephemeral=True,
12288
)
123-
search_body = r_search.json()
124-
items: list[dict[str, Any]] = search_body.get("items") or search_body.get("data") or []
12589
assert key_id in [item["id"] for item in items], (
12690
f"Ephemeral key {key_id} should appear in search with includeEphemeral=True"
12791
)
92+
LOGGER.info(f"[ephemeral] Ephemeral key {key_id} visible with includeEphemeral=True")
12893

129-
r_default = request_session_http.post(
130-
url=f"{api_keys_endpoint}/search",
131-
headers=auth_header,
132-
json={
133-
"filters": {"status": ["active"]},
134-
"pagination": {"limit": 50, "offset": 0},
135-
},
136-
timeout=30,
137-
)
138-
assert r_default.status_code == 200, (
139-
f"Expected 200 from default search, got {r_default.status_code}: {(r_default.text or '')[:200]}"
94+
@pytest.mark.tier1
95+
@pytest.mark.parametrize("ocp_token_for_actor", [{"type": "free"}], indirect=True)
96+
def test_ephemeral_key_hidden_from_default_search(
97+
self,
98+
request_session_http: requests.Session,
99+
base_url: str,
100+
ocp_token_for_actor: str,
101+
ephemeral_api_key: dict[str, Any],
102+
) -> None:
103+
"""Verify ephemeral key is hidden from default search when includeEphemeral is not set."""
104+
key_id = ephemeral_api_key["id"]
105+
106+
default_items = search_active_api_keys(
107+
request_session_http=request_session_http,
108+
base_url=base_url,
109+
ocp_user_token=ocp_token_for_actor,
110+
include_ephemeral=False,
140111
)
141-
default_body = r_default.json()
142-
default_items: list[dict[str, Any]] = default_body.get("items") or default_body.get("data") or []
143112
assert key_id not in [item["id"] for item in default_items], (
144113
"Ephemeral key should be excluded from default search (includeEphemeral defaults to False)"
145114
)
146-
LOGGER.info(f"[ephemeral] Ephemeral key {key_id} visibility verified")
115+
LOGGER.info(f"[ephemeral] Ephemeral key {key_id} correctly hidden from default search")
147116

148117
@pytest.mark.tier1
149118
@pytest.mark.parametrize("ocp_token_for_actor", [{"type": "free"}], indirect=True)

tests/model_serving/maas_billing/maas_subscription/utils.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,29 @@ def assert_api_key_created_ok(
472472
assert field in body, f"Response must contain '{field}'"
473473

474474

475+
def search_active_api_keys(
476+
request_session_http: requests.Session,
477+
base_url: str,
478+
ocp_user_token: str,
479+
include_ephemeral: bool = False,
480+
request_timeout_seconds: int = 30,
481+
) -> list[dict[str, Any]]:
482+
"""POST /v1/api-keys/search for active keys and return the list of matching items."""
483+
filters: dict[str, Any] = {"status": ["active"]}
484+
if include_ephemeral:
485+
filters["includeEphemeral"] = True
486+
url = f"{base_url}/v1/api-keys/search"
487+
resp = request_session_http.post(
488+
url=url,
489+
headers={"Authorization": f"Bearer {ocp_user_token}"},
490+
json={"filters": filters, "pagination": {"limit": 50, "offset": 0}},
491+
timeout=request_timeout_seconds,
492+
)
493+
assert resp.status_code == 200, f"Expected 200 from key search, got {resp.status_code}: {(resp.text or '')[:200]}"
494+
body = resp.json()
495+
return body.get("items") or body.get("data") or []
496+
497+
475498
def assert_api_key_get_ok(resp: Response, body: dict[str, Any], key_id: str) -> None:
476499
"""Assert a GET /v1/api-keys/{id} response has status 200."""
477500
assert resp.status_code == 200, (

0 commit comments

Comments
 (0)