Skip to content

Commit 14df149

Browse files
committed
MaaS billing: address review comments
1 parent a3c0bae commit 14df149

File tree

2 files changed

+176
-85
lines changed

2 files changed

+176
-85
lines changed

tests/model_serving/model_server/maas_billing/conftest.py

Lines changed: 134 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
from simple_logger.logger import get_logger
66
from utilities.plugins.constant import OpenAIEnpoints
77
from ocp_resources.service_account import ServiceAccount
8-
98
from kubernetes.dynamic import DynamicClient
109
from ocp_resources.namespace import Namespace
1110
from ocp_resources.llm_inference_service import LLMInferenceService
12-
11+
from ocp_resources.deployment import Deployment
12+
from timeout_sampler import TimeoutSampler
1313
from utilities.llmd_utils import create_llmisvc
1414
from utilities.llmd_constants import ModelStorage, ContainerImages
1515
from utilities.constants import (
@@ -29,6 +29,9 @@
2929
from ocp_resources.secret import Secret
3030
from tests.model_serving.model_server.maas_billing.utils import get_total_tokens
3131
from utilities.infra import get_data_science_cluster
32+
from utilities.constants import DscComponents
33+
from utilities.resources.rate_limit_policy import RateLimitPolicy
34+
from utilities.resources.token_rate_limit_policy import TokenRateLimitPolicy
3235
from tests.model_serving.model_server.maas_billing.utils import (
3336
detect_scheme_via_llmisvc,
3437
host_from_ingress_domain,
@@ -39,9 +42,12 @@
3942
get_maas_models_response,
4043
verify_chat_completions,
4144
maas_gateway_rate_limits_patched,
42-
ensure_maas_gateway_and_policies,
4345
detect_maas_control_plane_namespace,
4446
get_tier_mapping_configmap,
47+
ensure_maas_gateway_api,
48+
ensure_maas_usage_policies,
49+
endpoints_have_ready_addresses,
50+
gateway_probe_reaches_maas_api,
4551
)
4652

4753
LOGGER = get_logger(name=__name__)
@@ -64,7 +70,15 @@ def request_session_http() -> Generator[requests.Session, None, None]:
6470

6571

6672
@pytest.fixture(scope="class")
67-
def minted_token(request_session_http, base_url: str, current_client_token: str) -> str:
73+
def minted_token(
74+
request_session_http,
75+
base_url: str,
76+
current_client_token: str,
77+
maas_control_plane_namespace: str,
78+
maas_controller_enabled_latest: None,
79+
maas_gateway_and_policies: None,
80+
maas_api_ready: None,
81+
) -> str:
6882
"""Mint a MaaS token once per test class and reuse it."""
6983
resp, body = mint_token(
7084
base_url=base_url,
@@ -94,7 +108,7 @@ def model_url(
94108
) -> str:
95109
deployment = maas_inference_service_tinyllama.name
96110
url = f"{maas_scheme}://{maas_host}/llm/{deployment}{CHAT_COMPLETIONS}"
97-
LOGGER.info("MaaS: constructed model_url=%s (deployment=%s)", url, deployment)
111+
LOGGER.info(f"MaaS: constructed model_url={url} (deployment={deployment})")
98112
return url
99113

100114

@@ -440,8 +454,9 @@ def maas_token_for_actor(
440454
base_url: str,
441455
ocp_token_for_actor: str,
442456
maas_control_plane_namespace: str,
443-
maas_controller_enabled_latest,
444-
maas_gateway_and_policies,
457+
maas_controller_enabled_latest: None,
458+
maas_gateway_and_policies: None,
459+
maas_api_ready: None,
445460
) -> str:
446461
"""
447462
Mint a MaaS token once per actor (admin / free / premium) and reuse it
@@ -545,7 +560,7 @@ def maas_inference_service_tinyllama(
545560
unprivileged_model_namespace: Namespace,
546561
model_service_account: ServiceAccount,
547562
maas_control_plane_namespace: str,
548-
maas_gateway_and_policies,
563+
maas_gateway_and_policies: None,
549564
) -> Generator[LLMInferenceService, None, None]:
550565
"""
551566
TinyLlama S3-backed LLMInferenceService wired through MaaS for tests.
@@ -603,6 +618,8 @@ def maas_gateway_rate_limits(
603618
admin_client: DynamicClient,
604619
maas_gateway_and_policies,
605620
maas_tier_mapping_cm,
621+
maas_request_ratelimit_policy,
622+
maas_token_ratelimit_policy,
606623
) -> Generator[None, None, None]:
607624
with maas_gateway_rate_limits_patched(
608625
admin_client=admin_client,
@@ -622,38 +639,45 @@ def maas_gateway_api_hostname(admin_client: DynamicClient) -> str:
622639
def maas_gateway_and_policies(
623640
admin_client: DynamicClient,
624641
maas_gateway_api_hostname: str,
625-
maas_controller_enabled_latest,
626642
) -> Generator[None, None, None]:
627643
"""
628644
Ensure MaaS Gateway + Kuadrant policies exist once per test session.
629645
"""
630-
with ensure_maas_gateway_and_policies(
631-
admin_client=admin_client,
632-
hostname=maas_gateway_api_hostname,
646+
with (
647+
ensure_maas_gateway_api(
648+
admin_client=admin_client,
649+
hostname=maas_gateway_api_hostname,
650+
),
651+
ensure_maas_usage_policies(
652+
admin_client=admin_client,
653+
),
633654
):
634655
yield
635656

636657

637658
@pytest.fixture(scope="session")
638-
def maas_controller_enabled_latest(admin_client: DynamicClient):
659+
def maas_controller_enabled_latest(
660+
admin_client: DynamicClient,
661+
maas_gateway_and_policies: None,
662+
):
639663
dsc_resource = get_data_science_cluster(client=admin_client)
640664
dsc_resource.get()
641665

642666
original_components = dsc_resource.instance.spec.components
643667

644-
kserve = original_components.get("kserve") or {}
645-
maas = kserve.get("modelsAsService") or {}
646-
if (maas.get("managementState") or "Removed") == "Managed":
668+
kserve = original_components[DscComponents.KSERVE]
669+
maas = kserve["modelsAsService"]
670+
if maas["managementState"] == "Managed":
647671
dsc_resource.wait_for_condition(condition="ModelsAsServiceReady", status="True", timeout=Timeout.TIMEOUT_15MIN)
648672
yield dsc_resource
649-
return
650673

651-
component_patch = {"kserve": {"modelsAsService": {"managementState": "Managed"}}}
674+
component_patch = {DscComponents.KSERVE: {"modelsAsService": {"managementState": "Managed"}}}
652675

653676
with ResourceEditor(patches={dsc_resource: {"spec": {"components": component_patch}}}):
654677
dsc_resource.wait_for_condition(condition="ModelsAsServiceReady", status="True", timeout=Timeout.TIMEOUT_15MIN)
655678
dsc_resource.wait_for_condition(condition="Ready", status="True", timeout=Timeout.TIMEOUT_15MIN)
656679
yield dsc_resource
680+
dsc_resource.wait_for_condition(condition="Ready", status="True", timeout=Timeout.TIMEOUT_15MIN)
657681

658682

659683
@pytest.fixture(scope="session")
@@ -679,3 +703,95 @@ def maas_tier_mapping_cm(
679703
)
680704

681705
return config_map
706+
707+
708+
@pytest.fixture(scope="class")
709+
def maas_api_ready(
710+
admin_client: DynamicClient,
711+
request_session_http: requests.Session,
712+
base_url: str,
713+
) -> None:
714+
"""
715+
MaaS API readiness
716+
717+
"""
718+
maas_api_deployment = Deployment(
719+
client=admin_client,
720+
name="maas-api",
721+
namespace="opendatahub",
722+
)
723+
maas_api_deployment.wait_for_condition(
724+
condition="Available",
725+
status="True",
726+
timeout=Timeout.TIMEOUT_10MIN,
727+
)
728+
729+
endpoints_ready = False
730+
for endpoints_ready_sample in TimeoutSampler(
731+
wait_timeout=Timeout.TIMEOUT_5MIN,
732+
sleep=5,
733+
func=endpoints_have_ready_addresses,
734+
admin_client=admin_client,
735+
namespace="opendatahub",
736+
name="maas-api",
737+
):
738+
endpoints_ready = bool(endpoints_ready_sample)
739+
if endpoints_ready:
740+
break
741+
742+
if not endpoints_ready:
743+
pytest.fail("MaaS API endpoints are not ready: no endpoint addresses after waiting")
744+
745+
probe_url = f"{base_url}/v1/models"
746+
747+
last_status_code: int | None = None
748+
last_response_body: str | None = None
749+
750+
for gateway_probe_sample in TimeoutSampler(
751+
wait_timeout=Timeout.TIMEOUT_5MIN,
752+
sleep=5,
753+
func=gateway_probe_reaches_maas_api,
754+
http_session=request_session_http,
755+
probe_url=probe_url,
756+
request_timeout_seconds=30,
757+
):
758+
gateway_reachable, status_code, response_text = gateway_probe_sample
759+
last_status_code = status_code
760+
last_response_body = response_text
761+
762+
if gateway_reachable:
763+
return
764+
765+
pytest.fail(
766+
"MaaS API is not reachable via Gateway yet. "
767+
f"Last status={last_status_code}, "
768+
f"body={(last_response_body or '')[:200]}"
769+
)
770+
771+
772+
@pytest.fixture(scope="session")
773+
def maas_request_ratelimit_policy(
774+
admin_client: DynamicClient,
775+
maas_gateway_and_policies: None,
776+
) -> RateLimitPolicy:
777+
778+
return RateLimitPolicy(
779+
client=admin_client,
780+
name=MAAS_RATE_LIMIT_POLICY_NAME,
781+
namespace=MAAS_GATEWAY_NAMESPACE,
782+
ensure_exists=True,
783+
)
784+
785+
786+
@pytest.fixture(scope="session")
787+
def maas_token_ratelimit_policy(
788+
admin_client: DynamicClient,
789+
maas_gateway_and_policies: None,
790+
) -> TokenRateLimitPolicy:
791+
792+
return TokenRateLimitPolicy(
793+
client=admin_client,
794+
name=MAAS_TOKEN_RATE_LIMIT_POLICY_NAME,
795+
namespace=MAAS_GATEWAY_NAMESPACE,
796+
ensure_exists=True,
797+
)

0 commit comments

Comments
 (0)