|
4 | 4 | from typing import Any |
5 | 5 |
|
6 | 6 | import pytest |
| 7 | +import yaml |
7 | 8 | from kubernetes.dynamic import DynamicClient |
8 | 9 | from ocp_resources.config_map import ConfigMap |
9 | 10 | from ocp_resources.data_science_cluster import DataScienceCluster |
|
25 | 26 | from tests.model_registry.constants import ( |
26 | 27 | DB_BASE_RESOURCES_NAME, |
27 | 28 | DB_RESOURCE_NAME, |
| 29 | + DEFAULT_CUSTOM_MODEL_CATALOG, |
28 | 30 | KUBERBACPROXY_STR, |
29 | 31 | MR_INSTANCE_BASE_NAME, |
30 | 32 | MR_INSTANCE_NAME, |
31 | 33 | MR_OPERATOR_NAME, |
32 | 34 | ) |
| 35 | +from tests.model_registry.mcp_servers.constants import ( |
| 36 | + MCP_CATALOG_API_PATH, |
| 37 | + MCP_CATALOG_SOURCE, |
| 38 | + MCP_SERVERS_YAML, |
| 39 | +) |
33 | 40 | from tests.model_registry.utils import ( |
34 | 41 | generate_namespace_name, |
35 | 42 | get_byoidc_user_credentials, |
36 | 43 | get_model_registry_metadata_resources, |
37 | 44 | get_model_registry_objects, |
38 | 45 | get_rest_headers, |
39 | 46 | wait_for_default_resource_cleanedup, |
| 47 | + wait_for_mcp_catalog_api, |
| 48 | + wait_for_model_catalog_pod_ready_after_deletion, |
40 | 49 | ) |
41 | 50 | from utilities.constants import DscComponents, Labels |
42 | 51 | from utilities.general import ( |
@@ -466,3 +475,54 @@ def model_catalog_routes(admin_client: DynamicClient, model_registry_namespace: |
466 | 475 | return list( |
467 | 476 | Route.get(namespace=model_registry_namespace, label_selector="component=model-catalog", client=admin_client) |
468 | 477 | ) |
| 478 | + |
| 479 | + |
| 480 | +@pytest.fixture(scope="class") |
| 481 | +def mcp_catalog_rest_urls(model_registry_namespace: str, model_catalog_routes: list[Route]) -> list[str]: |
| 482 | + """Build MCP catalog REST URL from existing model catalog routes.""" |
| 483 | + assert model_catalog_routes, f"Model catalog routes do not exist in {model_registry_namespace}" |
| 484 | + return [f"https://{route.instance.spec.host}:443{MCP_CATALOG_API_PATH}" for route in model_catalog_routes] |
| 485 | + |
| 486 | + |
| 487 | +@pytest.fixture(scope="class") |
| 488 | +def mcp_servers_configmap_patch( |
| 489 | + admin_client: DynamicClient, |
| 490 | + model_registry_namespace: str, |
| 491 | + mcp_catalog_rest_urls: list[str], |
| 492 | + model_registry_rest_headers: dict[str, str], |
| 493 | +) -> Generator[None]: |
| 494 | + """ |
| 495 | + Class-scoped fixture that patches the model-catalog-sources ConfigMap |
| 496 | +
|
| 497 | + Sets two keys in the ConfigMap data: |
| 498 | + - sources.yaml: catalog source definition pointing to the MCP servers YAML |
| 499 | + - mcp-servers.yaml: the actual MCP server definitions |
| 500 | + """ |
| 501 | + catalog_config_map = ConfigMap( |
| 502 | + name=DEFAULT_CUSTOM_MODEL_CATALOG, |
| 503 | + client=admin_client, |
| 504 | + namespace=model_registry_namespace, |
| 505 | + ) |
| 506 | + |
| 507 | + current_data = yaml.safe_load(catalog_config_map.instance.data.get("sources.yaml", "{}") or "{}") |
| 508 | + if "mcp_catalogs" not in current_data: |
| 509 | + current_data["mcp_catalogs"] = [] |
| 510 | + current_data["mcp_catalogs"].append(MCP_CATALOG_SOURCE) |
| 511 | + |
| 512 | + patches = { |
| 513 | + "data": { |
| 514 | + "sources.yaml": yaml.dump(current_data, default_flow_style=False), |
| 515 | + "mcp-servers.yaml": MCP_SERVERS_YAML, |
| 516 | + } |
| 517 | + } |
| 518 | + |
| 519 | + with ResourceEditor(patches={catalog_config_map: patches}): |
| 520 | + wait_for_model_catalog_pod_ready_after_deletion( |
| 521 | + client=admin_client, model_registry_namespace=model_registry_namespace |
| 522 | + ) |
| 523 | + wait_for_mcp_catalog_api(url=mcp_catalog_rest_urls[0], headers=model_registry_rest_headers) |
| 524 | + yield |
| 525 | + |
| 526 | + wait_for_model_catalog_pod_ready_after_deletion( |
| 527 | + client=admin_client, model_registry_namespace=model_registry_namespace |
| 528 | + ) |
0 commit comments