Skip to content

Commit 34db274

Browse files
authored
test: optimize API readiness check in model catalog conftest (#919)
* test: optimize API readiness check in model catalog conftest * test: improve API token authorization handling - Extract URL and header creation into reusable utility function - Add explicit token authorization wait for service account users * fix: typo in comment
1 parent 5ade275 commit 34db274

3 files changed

Lines changed: 54 additions & 19 deletions

File tree

tests/model_registry/model_catalog/conftest.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99

1010
from ocp_resources.config_map import ConfigMap
1111
from ocp_resources.resource import ResourceEditor
12-
1312
from ocp_resources.service_account import ServiceAccount
1413
from tests.model_registry.model_catalog.constants import (
1514
SAMPLE_MODEL_NAME3,
1615
DEFAULT_CATALOG_FILE,
1716
CATALOG_CONTAINER,
1817
REDHAT_AI_CATALOG_ID,
1918
)
20-
from tests.model_registry.model_catalog.utils import get_models_from_catalog_api
19+
from tests.model_registry.model_catalog.utils import get_models_from_catalog_api, get_catalog_url_and_headers
2120
from tests.model_registry.constants import (
2221
CUSTOM_CATALOG_ID1,
2322
DEFAULT_MODEL_CATALOG_CM,
@@ -42,6 +41,7 @@
4241
def enabled_model_catalog_config_map(
4342
admin_client: DynamicClient,
4443
model_registry_namespace: str,
44+
current_client_token: str,
4545
) -> ConfigMap:
4646
"""
4747
Enable all catalogs in the default model catalog configmap
@@ -74,7 +74,17 @@ def enabled_model_catalog_config_map(
7474
patches = {"data": {"sources.yaml": enabled_sources_yaml}}
7575

7676
with ResourceEditor(patches={user_sources_cm: patches}):
77+
# Wait for the model catalog pod to be ready
7778
is_model_catalog_ready(client=admin_client, model_registry_namespace=model_registry_namespace)
79+
80+
# Get the model catalog URL and headers and wait for the API to be fully ready
81+
catalog_url, headers = get_catalog_url_and_headers(
82+
admin_client=admin_client,
83+
model_registry_namespace=model_registry_namespace,
84+
token=current_client_token,
85+
)
86+
wait_for_model_catalog_api(url=catalog_url, headers=headers)
87+
7888
yield user_sources_cm
7989

8090

@@ -145,33 +155,44 @@ def user_token_for_api_calls(
145155
api_server_url: str,
146156
user_credentials_rbac: dict[str, str],
147157
service_account: ServiceAccount,
158+
model_catalog_rest_url: list[str],
148159
) -> Generator[str, None, None]:
149160
param = getattr(request, "param", {})
150161
user = param.get("user_type", "admin")
151162
LOGGER.info("User used: %s", user)
163+
164+
token = None
152165
if user == "admin":
153166
LOGGER.info("Logging in as admin user")
154-
yield get_openshift_token()
167+
token = get_openshift_token()
155168
elif user == "test":
156169
if not is_byoidc:
157170
login_with_user_password(
158171
api_address=api_server_url,
159172
user=user_credentials_rbac["username"],
160173
password=user_credentials_rbac["password"],
161174
)
162-
yield get_openshift_token()
163-
LOGGER.info(f"Logging in as {original_user}")
164-
login_with_user_password(
165-
api_address=api_server_url,
166-
user=original_user,
167-
)
175+
token = get_openshift_token()
168176
else:
169-
yield get_mr_user_token(admin_client=admin_client, user_credentials_rbac=user_credentials_rbac)
177+
token = get_mr_user_token(admin_client=admin_client, user_credentials_rbac=user_credentials_rbac)
170178
elif user == "sa_user":
171-
yield create_inference_token(service_account)
179+
token = create_inference_token(service_account)
180+
# retries on 401 errors for OAuth/kube-rbac-proxy initialization
181+
headers = get_rest_headers(token=token)
182+
wait_for_model_catalog_api(url=model_catalog_rest_url[0], headers=headers)
172183
else:
173184
raise RuntimeError(f"Unknown user type: {user}")
174185

186+
yield token
187+
188+
# Cleanup: log back in as original user if needed
189+
if user == "test" and not is_byoidc:
190+
LOGGER.info(f"Logging in as {original_user}")
191+
login_with_user_password(
192+
api_address=api_server_url,
193+
user=original_user,
194+
)
195+
175196

176197
@pytest.fixture(scope="function")
177198
def randomly_picked_model_from_catalog_api_by_source(
@@ -203,7 +224,6 @@ def randomly_picked_model_from_catalog_api_by_source(
203224
headers = model_registry_rest_headers
204225
else:
205226
headers = get_rest_headers(token=user_token_for_api_calls)
206-
wait_for_model_catalog_api(url=f"{model_catalog_rest_url[0]}", headers=headers)
207227

208228
if not model_name:
209229
LOGGER.info(f"Picking random model from catalog: {catalog_id} with header_type: {header_type}")

tests/model_registry/model_catalog/utils.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

44
from kubernetes.dynamic import DynamicClient
55
from simple_logger.logger import get_logger
6-
6+
from timeout_sampler import retry
77

88
from ocp_resources.pod import Pod
99
from ocp_resources.config_map import ConfigMap
10+
from ocp_resources.route import Route
1011
from tests.model_registry.model_catalog.constants import DEFAULT_CATALOGS
1112
from tests.model_registry.model_catalog.db_constants import (
1213
SEARCH_MODELS_DB_QUERY,
@@ -23,8 +24,7 @@
2324
PERFORMANCE_DATA_DIR,
2425
)
2526
from tests.model_registry.constants import DEFAULT_CUSTOM_MODEL_CATALOG, DEFAULT_MODEL_CATALOG_CM
26-
from tests.model_registry.utils import execute_get_command
27-
from tests.model_registry.utils import get_rest_headers
27+
from tests.model_registry.utils import execute_get_command, get_rest_headers
2828

2929
LOGGER = get_logger(name=__name__)
3030

@@ -33,6 +33,24 @@ class ResourceNotFoundError(Exception):
3333
pass
3434

3535

36+
@retry(wait_timeout=60, sleep=5, exceptions_dict={AssertionError: []})
37+
def get_catalog_url_and_headers(
38+
admin_client: DynamicClient,
39+
model_registry_namespace: str,
40+
token: str,
41+
) -> tuple[str, dict[str, str]]:
42+
"""
43+
Get model catalog URL and authentication headers from route.
44+
"""
45+
model_catalog_routes = list(
46+
Route.get(namespace=model_registry_namespace, label_selector="component=model-catalog", dyn_client=admin_client)
47+
)
48+
assert model_catalog_routes, f"Model catalog routes not found in namespace {model_registry_namespace}"
49+
50+
catalog_url = f"https://{model_catalog_routes[0].instance.spec.host}:443/api/model_catalog/v1alpha1/"
51+
return catalog_url, get_rest_headers(token=token)
52+
53+
3654
def validate_model_catalog_enabled(pod: Pod) -> bool:
3755
for container in pod.instance.spec.containers:
3856
for env in container.env:
@@ -1024,9 +1042,6 @@ def verify_custom_properties_sorted(items: list[dict], property_field: str, sort
10241042
10251043
Returns:
10261044
True if sorted correctly, False otherwise
1027-
1028-
Raises:
1029-
ValueError: If there are not enough items to verify sorting
10301045
"""
10311046
property_name, value_type = property_field.rsplit(".", 1)
10321047
# Separate items into two groups

tests/model_registry/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ def execute_get_call(
715715
return resp
716716

717717

718-
@retry(wait_timeout=60, sleep=5, exceptions_dict={ResourceNotFoundError: [], TransientUnauthorizedError: []})
718+
@retry(wait_timeout=90, sleep=5, exceptions_dict={ResourceNotFoundError: [], TransientUnauthorizedError: []})
719719
def wait_for_model_catalog_api(url: str, headers: dict[str, str], verify: bool | str = False) -> requests.Response:
720720
"""
721721
Wait for model catalog API to be ready and fully initialized checks both /sources and /models endpoints

0 commit comments

Comments
 (0)