11import base64
2+ import secrets
3+ import string
24from collections .abc import Generator
5+ from contextlib import ExitStack
36from typing import Any
47
58import pytest
1215from ocp_resources .gateway_gateway_networking_k8s_io import Gateway
1316from ocp_resources .infrastructure import Infrastructure
1417from ocp_resources .llm_inference_service import LLMInferenceService
18+ from ocp_resources .maas_auth_policy import MaaSAuthPolicy
19+ from ocp_resources .maas_model_ref import MaaSModelRef
20+ from ocp_resources .maas_subscription import MaaSSubscription
1521from ocp_resources .namespace import Namespace
1622from ocp_resources .oauth import OAuth
1723from ocp_resources .resource import ResourceEditor
1824from ocp_resources .secret import Secret
25+ from ocp_resources .service import Service
1926from ocp_resources .service_account import ServiceAccount
2027from pytest import FixtureRequest
2128from pytest_testconfig import config as py_config
2229from timeout_sampler import TimeoutSampler
2330
31+ from tests .model_serving .maas_billing .maas_subscription .utils import (
32+ MAAS_DB_NAMESPACE ,
33+ MAAS_SUBSCRIPTION_NAMESPACE ,
34+ get_maas_postgres_resources ,
35+ patch_llmisvc_with_maas_router_and_tiers ,
36+ wait_for_postgres_connection_log ,
37+ wait_for_postgres_deployment_ready ,
38+ )
2439from tests .model_serving .maas_billing .utils import (
2540 build_maas_headers ,
2641 create_maas_group ,
@@ -828,7 +843,10 @@ def maas_gateway_api(
828843 if gw .exists :
829844 LOGGER .info (f"Reusing existing gateway { MAAS_GATEWAY_NAMESPACE } /{ MAAS_GATEWAY_NAME } " )
830845 gw .wait_for_condition (condition = "Programmed" , status = "True" , timeout = 300 )
831- yield
846+ with ResourceEditor (
847+ patches = {gw : {"metadata" : {"annotations" : {"security.opendatahub.io/authorino-tls-bootstrap" : "true" }}}}
848+ ):
849+ yield
832850 else :
833851 LOGGER .info (f"Creating gateway { MAAS_GATEWAY_NAMESPACE } /{ MAAS_GATEWAY_NAME } " )
834852 with Gateway (
@@ -837,7 +855,10 @@ def maas_gateway_api(
837855 namespace = MAAS_GATEWAY_NAMESPACE ,
838856 gateway_class_name = "openshift-default" ,
839857 listeners = maas_gateway_listeners (hostname = maas_gateway_api_hostname ),
840- annotations = {"opendatahub.io/managed" : "false" },
858+ annotations = {
859+ "opendatahub.io/managed" : "false" ,
860+ "security.opendatahub.io/authorino-tls-bootstrap" : "true" ,
861+ },
841862 label = {
842863 "app.kubernetes.io/name" : "maas" ,
843864 "app.kubernetes.io/instance" : MAAS_GATEWAY_NAME ,
@@ -950,3 +971,222 @@ def revoke_maas_tokens_for_actor(
950971 )
951972
952973 LOGGER .info (f"[{ actor_label } ] revoke response: status={ r_del .status_code } body={ (r_del .text or '' )[:200 ]} " )
974+
975+
976+ @pytest .fixture (scope = "session" )
977+ def maas_postgres_credentials () -> dict [str , str ]:
978+ alphabet = string .ascii_letters + string .digits
979+
980+ postgres_user = f"maas-{ generate_random_name ()} "
981+ postgres_db = f"maas-{ generate_random_name ()} "
982+ postgres_password = "" .join (secrets .choice (alphabet ) for _ in range (32 ))
983+
984+ LOGGER .info (
985+ f"Generated PostgreSQL test credentials: "
986+ f"user={ postgres_user } , db={ postgres_db } , password_length={ len (postgres_password )} "
987+ )
988+
989+ return {
990+ "postgres_user" : postgres_user ,
991+ "postgres_password" : postgres_password ,
992+ "postgres_db" : postgres_db ,
993+ }
994+
995+
996+ @pytest .fixture (scope = "session" )
997+ def maas_postgres_prereqs (
998+ admin_client : DynamicClient ,
999+ maas_postgres_credentials : dict [str , str ],
1000+ ) -> Generator [dict [Any , Any ], Any , Any ]:
1001+ """
1002+ Prepare PostgreSQL resources required by maas-api before MaaS API key tests run.
1003+ """
1004+ resources = get_maas_postgres_resources (
1005+ client = admin_client ,
1006+ namespace = MAAS_DB_NAMESPACE ,
1007+ teardown_resources = True ,
1008+ postgres_user = maas_postgres_credentials ["postgres_user" ],
1009+ postgres_password = maas_postgres_credentials ["postgres_password" ],
1010+ postgres_db = maas_postgres_credentials ["postgres_db" ],
1011+ )
1012+
1013+ resources_instances : dict [Any , Any ] = {}
1014+
1015+ with ExitStack () as stack :
1016+ for kind_name in [Secret , Service , Deployment ]:
1017+ resources_instances [kind_name ] = []
1018+
1019+ for resource_obj in resources [kind_name ]:
1020+ resources_instances [kind_name ].append (stack .enter_context (resource_obj ))
1021+
1022+ for deployment in resources_instances [Deployment ]:
1023+ deployment .wait_for_condition (condition = "Available" , status = "True" , timeout = 180 )
1024+
1025+ wait_for_postgres_deployment_ready (
1026+ admin_client = admin_client ,
1027+ namespace = MAAS_DB_NAMESPACE ,
1028+ timeout = 180 ,
1029+ )
1030+ wait_for_postgres_connection_log (
1031+ admin_client = admin_client ,
1032+ namespace = MAAS_DB_NAMESPACE ,
1033+ timeout = 180 ,
1034+ )
1035+
1036+ yield resources_instances
1037+
1038+
1039+ @pytest .fixture (scope = "session" )
1040+ def maas_subscription_namespace (
1041+ unprivileged_client : DynamicClient , admin_client : DynamicClient
1042+ ) -> Generator [Namespace , Any , Any ]:
1043+ existing_ns = Namespace (client = admin_client , name = MAAS_SUBSCRIPTION_NAMESPACE )
1044+ if existing_ns .exists :
1045+ LOGGER .info (f"Namespace { MAAS_SUBSCRIPTION_NAMESPACE } already exists, reusing it" )
1046+ yield existing_ns
1047+ else :
1048+ with create_ns (
1049+ name = MAAS_SUBSCRIPTION_NAMESPACE ,
1050+ unprivileged_client = unprivileged_client ,
1051+ admin_client = admin_client ,
1052+ ) as ns :
1053+ yield ns
1054+
1055+
1056+ @pytest .fixture (scope = "session" )
1057+ def maas_subscription_controller_enabled_latest (
1058+ dsc_resource : DataScienceCluster ,
1059+ maas_postgres_prereqs : None ,
1060+ maas_gateway_api : None ,
1061+ maas_subscription_namespace : Namespace ,
1062+ ) -> Generator [DataScienceCluster , Any , Any ]:
1063+ """
1064+ ensures postgres prerequisites and subscription namespace exist before MaaS is switched to Managed.
1065+ """
1066+ component_patch = {
1067+ DscComponents .KSERVE : {"modelsAsService" : {"managementState" : DscComponents .ManagementState .MANAGED }}
1068+ }
1069+
1070+ with ResourceEditor (patches = {dsc_resource : {"spec" : {"components" : component_patch }}}):
1071+ dsc_resource .wait_for_condition (
1072+ condition = "ModelsAsServiceReady" ,
1073+ status = "True" ,
1074+ timeout = 900 ,
1075+ )
1076+ dsc_resource .wait_for_condition (
1077+ condition = "Ready" ,
1078+ status = "True" ,
1079+ timeout = 600 ,
1080+ )
1081+ yield dsc_resource
1082+
1083+ dsc_resource .wait_for_condition (condition = "Ready" , status = "True" , timeout = 600 )
1084+
1085+
1086+ @pytest .fixture (scope = "class" )
1087+ def maas_inference_service_tinyllama_free (
1088+ admin_client : DynamicClient ,
1089+ maas_unprivileged_model_namespace : Namespace ,
1090+ maas_model_service_account : ServiceAccount ,
1091+ maas_gateway_api : None ,
1092+ ) -> Generator [LLMInferenceService , Any , Any ]:
1093+ with (
1094+ create_llmisvc (
1095+ client = admin_client ,
1096+ name = "llm-s3-tinyllama-free" ,
1097+ namespace = maas_unprivileged_model_namespace .name ,
1098+ storage_uri = ModelStorage .TINYLLAMA_S3 ,
1099+ container_image = ContainerImages .VLLM_CPU ,
1100+ container_resources = {
1101+ "limits" : {"cpu" : "2" , "memory" : "12Gi" },
1102+ "requests" : {"cpu" : "1" , "memory" : "8Gi" },
1103+ },
1104+ service_account = maas_model_service_account .name ,
1105+ wait = False ,
1106+ timeout = 900 ,
1107+ ) as llm_service ,
1108+ patch_llmisvc_with_maas_router_and_tiers (llm_service = llm_service , tiers = []),
1109+ ):
1110+ llm_service .wait_for_condition (condition = "Ready" , status = "True" , timeout = 900 )
1111+ yield llm_service
1112+
1113+
1114+ @pytest .fixture (scope = "class" )
1115+ def maas_model_tinyllama_free (
1116+ admin_client : DynamicClient ,
1117+ maas_inference_service_tinyllama_free : LLMInferenceService ,
1118+ ) -> Generator [MaaSModelRef , Any , Any ]:
1119+
1120+ with MaaSModelRef (
1121+ client = admin_client ,
1122+ name = maas_inference_service_tinyllama_free .name ,
1123+ namespace = maas_inference_service_tinyllama_free .namespace ,
1124+ model_ref = {
1125+ "name" : maas_inference_service_tinyllama_free .name ,
1126+ "namespace" : maas_inference_service_tinyllama_free .namespace ,
1127+ "kind" : "LLMInferenceService" ,
1128+ },
1129+ teardown = True ,
1130+ wait_for_resource = True ,
1131+ ) as maas_model :
1132+ yield maas_model
1133+
1134+
1135+ @pytest .fixture (scope = "class" )
1136+ def maas_auth_policy_tinyllama_free (
1137+ admin_client : DynamicClient ,
1138+ maas_free_group : str ,
1139+ maas_model_tinyllama_free : MaaSModelRef ,
1140+ maas_subscription_namespace : Namespace ,
1141+ ) -> Generator [MaaSAuthPolicy , Any , Any ]:
1142+
1143+ with MaaSAuthPolicy (
1144+ client = admin_client ,
1145+ name = "tinyllama-free-access" ,
1146+ namespace = maas_subscription_namespace .name ,
1147+ model_refs = [
1148+ {
1149+ "name" : maas_model_tinyllama_free .name ,
1150+ "namespace" : maas_model_tinyllama_free .namespace ,
1151+ }
1152+ ],
1153+ subjects = {
1154+ "groups" : [
1155+ {"name" : "system:authenticated" },
1156+ {"name" : maas_free_group },
1157+ ],
1158+ },
1159+ teardown = True ,
1160+ wait_for_resource = True ,
1161+ ) as maas_auth_policy_free :
1162+ yield maas_auth_policy_free
1163+
1164+
1165+ @pytest .fixture (scope = "class" )
1166+ def maas_subscription_tinyllama_free (
1167+ admin_client : DynamicClient ,
1168+ maas_free_group : str ,
1169+ maas_model_tinyllama_free : MaaSModelRef ,
1170+ maas_subscription_namespace : Namespace ,
1171+ ) -> Generator [MaaSSubscription , Any , Any ]:
1172+
1173+ with MaaSSubscription (
1174+ client = admin_client ,
1175+ name = "tinyllama-free-subscription" ,
1176+ namespace = maas_subscription_namespace .name ,
1177+ owner = {
1178+ "groups" : [{"name" : maas_free_group }],
1179+ },
1180+ model_refs = [
1181+ {
1182+ "name" : maas_model_tinyllama_free .name ,
1183+ "namespace" : maas_model_tinyllama_free .namespace ,
1184+ "tokenRateLimits" : [{"limit" : 100 , "window" : "1m" }],
1185+ }
1186+ ],
1187+ priority = 0 ,
1188+ teardown = True ,
1189+ wait_for_resource = True ,
1190+ ) as maas_subscription_free :
1191+ maas_subscription_free .wait_for_condition (condition = "Ready" , status = "True" , timeout = 300 )
1192+ yield maas_subscription_free
0 commit comments