Skip to content

Commit c0bd288

Browse files
committed
Merge remote-tracking branch 'upstream/main' into archive_model
2 parents 8195a8f + a21ceb3 commit c0bd288

12 files changed

Lines changed: 877 additions & 655 deletions

File tree

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ repos:
3535
- id: detect-secrets
3636

3737
- repo: https://github.com/astral-sh/ruff-pre-commit
38-
rev: v0.11.11
38+
rev: v0.11.12
3939
hooks:
4040
- id: ruff
4141
- id: ruff-format
@@ -49,12 +49,12 @@ repos:
4949
# - id: renovate-config-validator
5050

5151
- repo: https://github.com/gitleaks/gitleaks
52-
rev: v8.26.0
52+
rev: v8.27.0
5353
hooks:
5454
- id: gitleaks
5555

5656
- repo: https://github.com/pre-commit/mirrors-mypy
57-
rev: v1.15.0
57+
rev: v1.16.0
5858
hooks:
5959
- id: mypy
6060
additional_dependencies: ["types-PyYAML", "types-requests"]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ dependencies = [
6363
"timeout-sampler>=1.0.6",
6464
"shortuuid>=1.0.13",
6565
"jira>=3.8.0",
66-
"openshift-python-wrapper>=11.0.50",
66+
"openshift-python-wrapper>=11.0.57",
6767
"semver>=3.0.4",
6868
"sqlalchemy>=2.0.40",
6969
"pytest-order>=1.3.0",

tests/model_registry/conftest.py

Lines changed: 109 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from ocp_resources.data_science_cluster import DataScienceCluster
1010
from ocp_resources.deployment import Deployment
1111

12-
from ocp_resources.model_registry import ModelRegistry
12+
from ocp_resources.model_registry_modelregistry_opendatahub_io import ModelRegistry
1313
from schemathesis.specs.openapi.schemas import BaseOpenAPISchema
1414
from schemathesis.generation.stateful.state_machine import APIStateMachine
1515
from schemathesis.core.transport import Response
@@ -29,6 +29,8 @@
2929
DB_RESOURCES_NAME,
3030
MODEL_REGISTRY_DB_SECRET_STR_DATA,
3131
MODEL_REGISTRY_DB_SECRET_ANNOTATIONS,
32+
OAUTH_PROXY_CONFIG_DICT,
33+
MODEL_REGISTRY_STANDARD_LABELS,
3234
)
3335
from utilities.constants import Labels
3436
from tests.model_registry.utils import (
@@ -37,8 +39,9 @@
3739
get_model_registry_deployment_template_dict,
3840
get_model_registry_db_label_dict,
3941
wait_for_pods_running,
42+
create_model_registry_instance,
4043
)
41-
from utilities.constants import Annotations, Protocols, DscComponents
44+
from utilities.constants import Protocols, DscComponents
4245
from model_registry import ModelRegistry as ModelRegistryClient
4346
from semver import Version
4447
from utilities.infra import get_product_version
@@ -145,53 +148,113 @@ def model_registry_db_deployment(
145148

146149
@pytest.fixture(scope="class")
147150
def model_registry_instance(
148-
admin_client: DynamicClient,
149151
model_registry_namespace: str,
150-
model_registry_db_deployment: Deployment,
151-
model_registry_db_secret: Secret,
152-
model_registry_db_service: Service,
152+
model_registry_mysql_config: dict[str, Any],
153153
) -> Generator[ModelRegistry, Any, Any]:
154-
with ModelRegistry(
155-
name=MR_INSTANCE_NAME,
154+
"""Creates a model registry instance with service mesh configuration."""
155+
with create_model_registry_instance(
156156
namespace=model_registry_namespace,
157-
label={
158-
Annotations.KubernetesIo.NAME: MR_INSTANCE_NAME,
159-
Annotations.KubernetesIo.INSTANCE: MR_INSTANCE_NAME,
160-
Annotations.KubernetesIo.PART_OF: MR_OPERATOR_NAME,
161-
Annotations.KubernetesIo.CREATED_BY: MR_OPERATOR_NAME,
162-
},
157+
name=MR_INSTANCE_NAME,
158+
labels=MODEL_REGISTRY_STANDARD_LABELS,
163159
grpc={},
164160
rest={},
165161
istio=ISTIO_CONFIG_DICT,
166-
mysql={
167-
"host": f"{model_registry_db_deployment.name}.{model_registry_db_deployment.namespace}.svc.cluster.local",
168-
"database": model_registry_db_secret.string_data["database-name"],
169-
"passwordSecret": {"key": "database-password", "name": DB_RESOURCES_NAME},
170-
"port": 3306,
171-
"skipDBCreation": False,
172-
"username": model_registry_db_secret.string_data["database-user"],
173-
},
162+
mysql=model_registry_mysql_config,
163+
wait_for_resource=True,
164+
) as mr:
165+
mr.wait_for_condition(condition="Available", status="True")
166+
yield mr
167+
168+
169+
@pytest.fixture(scope="class")
170+
def model_registry_instance_oauth_proxy(
171+
model_registry_namespace: str,
172+
model_registry_mysql_config: dict[str, Any],
173+
) -> Generator[ModelRegistry, Any, Any]:
174+
"""Creates a model registry instance with oauth proxy configuration."""
175+
with create_model_registry_instance(
176+
namespace=model_registry_namespace,
177+
name=MR_INSTANCE_NAME,
178+
labels=MODEL_REGISTRY_STANDARD_LABELS,
179+
grpc={},
180+
rest={},
181+
oauth_proxy=OAUTH_PROXY_CONFIG_DICT,
182+
mysql=model_registry_mysql_config,
174183
wait_for_resource=True,
175184
) as mr:
176185
mr.wait_for_condition(condition="Available", status="True")
177186
yield mr
178187

179188

189+
@pytest.fixture(scope="class")
190+
def model_registry_mysql_config(
191+
model_registry_db_deployment: Deployment, model_registry_db_secret: Secret
192+
) -> dict[str, Any]:
193+
return {
194+
"host": f"{model_registry_db_deployment.name}.{model_registry_db_deployment.namespace}.svc.cluster.local",
195+
"database": model_registry_db_secret.string_data["database-name"],
196+
"passwordSecret": {"key": "database-password", "name": model_registry_db_deployment.name},
197+
"port": 3306,
198+
"skipDBCreation": False,
199+
"username": model_registry_db_secret.string_data["database-user"],
200+
}
201+
202+
180203
@pytest.fixture(scope="class")
181204
def model_registry_instance_service(
182205
admin_client: DynamicClient,
183206
model_registry_namespace: str,
184207
model_registry_instance: ModelRegistry,
185208
) -> Service:
209+
"""
210+
Get the service for the regular model registry instance.
211+
Args:
212+
admin_client: The admin client
213+
model_registry_namespace: The namespace where the model registry is deployed
214+
model_registry_instance: The model registry instance to get the service for
215+
Returns:
216+
Service: The service for the model registry instance
217+
"""
218+
return get_mr_service_by_label(
219+
client=admin_client,
220+
ns=Namespace(name=model_registry_namespace),
221+
mr_instance=model_registry_instance,
222+
)
223+
224+
225+
@pytest.fixture(scope="class")
226+
def model_registry_instance_oauth_service(
227+
admin_client: DynamicClient,
228+
model_registry_namespace: str,
229+
model_registry_instance_oauth_proxy: ModelRegistry,
230+
) -> Service:
231+
"""
232+
Get the service for the OAuth proxy model registry instance.
233+
Args:
234+
admin_client: The admin client
235+
model_registry_namespace: The namespace where the model registry is deployed
236+
model_registry_instance_oauth_proxy: The OAuth proxy model registry instance
237+
Returns:
238+
Service: The service for the OAuth proxy model registry instance
239+
"""
186240
return get_mr_service_by_label(
187-
client=admin_client, ns=Namespace(name=model_registry_namespace), mr_instance=model_registry_instance
241+
client=admin_client,
242+
ns=Namespace(name=model_registry_namespace),
243+
mr_instance=model_registry_instance_oauth_proxy,
188244
)
189245

190246

191247
@pytest.fixture(scope="class")
192248
def model_registry_instance_rest_endpoint(
193249
model_registry_instance_service: Service,
194250
) -> str:
251+
"""
252+
Get the REST endpoint for the model registry instance.
253+
Args:
254+
model_registry_instance_service: The service for the model registry instance
255+
Returns:
256+
str: The REST endpoint for the model registry instance
257+
"""
195258
return get_endpoint_from_mr_service(svc=model_registry_instance_service, protocol=Protocols.REST)
196259

197260

@@ -262,9 +325,29 @@ def updated_dsc_component_state_scope_class(
262325

263326

264327
@pytest.fixture(scope="class")
265-
def model_registry_client(current_client_token: str, model_registry_instance_rest_endpoint: str) -> ModelRegistryClient:
266-
# address and port need to be split in the client instantiation
267-
server, port = model_registry_instance_rest_endpoint.split(":")
328+
def model_registry_client(
329+
request: FixtureRequest,
330+
current_client_token: str,
331+
) -> ModelRegistryClient:
332+
"""
333+
Get a client for the model registry instance.
334+
Args:
335+
request: The pytest request object
336+
current_client_token: The current client token
337+
Returns:
338+
ModelRegistryClient: A client for the model registry instance
339+
"""
340+
# Get the service fixture name from the request
341+
service_fixture = getattr(request, "param", {}).get("service_fixture", "model_registry_instance_service")
342+
343+
# Get the service from the fixture
344+
service = request.getfixturevalue(argname=service_fixture)
345+
346+
# Get the REST endpoint
347+
rest_endpoint = get_endpoint_from_mr_service(svc=service, protocol=Protocols.REST)
348+
349+
# Create the client
350+
server, port = rest_endpoint.split(":")
268351
return ModelRegistryClient(
269352
server_address=f"{Protocols.HTTPS}://{server}",
270353
port=port,

tests/model_registry/constants.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any
22
from ocp_resources.resource import Resource
3-
from utilities.constants import ModelFormat
3+
from utilities.constants import ModelFormat, Annotations
44

55

66
class ModelRegistryEndpoints:
@@ -29,6 +29,11 @@ class ModelRegistryEndpoints:
2929
ISTIO_CONFIG_DICT: dict[str, Any] = {
3030
"gateway": {"grpc": {"tls": {}}, "rest": {"tls": {}}},
3131
}
32+
OAUTH_PROXY_CONFIG_DICT: dict[str, Any] = {
33+
"port": 8443,
34+
"routePort": 443,
35+
"serviceRoute": "enabled",
36+
}
3237
DB_RESOURCES_NAME: str = "db-model-registry"
3338
MR_DB_IMAGE_DIGEST: str = (
3439
"public.ecr.aws/docker/library/mysql@sha256:9de9d54fecee6253130e65154b930978b1fcc336bcc86dfd06e89b72a2588ebe"
@@ -43,3 +48,9 @@ class ModelRegistryEndpoints:
4348
f"{Resource.ApiGroup.TEMPLATE_OPENSHIFT_IO}/expose-password": "'{.data[''database-password'']}'",
4449
f"{Resource.ApiGroup.TEMPLATE_OPENSHIFT_IO}/expose-username": "'{.data[''database-user'']}'",
4550
}
51+
MODEL_REGISTRY_STANDARD_LABELS = {
52+
Annotations.KubernetesIo.NAME: MR_INSTANCE_NAME,
53+
Annotations.KubernetesIo.INSTANCE: MR_INSTANCE_NAME,
54+
Annotations.KubernetesIo.PART_OF: MR_OPERATOR_NAME,
55+
Annotations.KubernetesIo.CREATED_BY: MR_OPERATOR_NAME,
56+
}

tests/model_registry/image_validation/test_verify_rhoai_images.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from utilities.general import (
99
validate_container_images,
1010
)
11-
from ocp_resources.model_registry import ModelRegistry
11+
from ocp_resources.model_registry_modelregistry_opendatahub_io import ModelRegistry
1212
from ocp_resources.pod import Pod
1313

1414
LOGGER = get_logger(name=__name__)

tests/model_registry/negative_tests/test_model_registry_creation_negative.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from simple_logger.logger import get_logger
44
from ocp_resources.data_science_cluster import DataScienceCluster
55
from ocp_resources.deployment import Deployment
6-
from ocp_resources.model_registry import ModelRegistry
6+
from ocp_resources.model_registry_modelregistry_opendatahub_io import ModelRegistry
77
from pytest_testconfig import config as py_config
88
from ocp_resources.namespace import Namespace
99
from ocp_resources.secret import Secret
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import pytest
2+
from typing import Self
3+
from simple_logger.logger import get_logger
4+
from pytest_testconfig import config as py_config
5+
from semver import Version
6+
from utilities.infra import get_product_version
7+
from utilities.constants import DscComponents
8+
from tests.model_registry.constants import MODEL_NAME, MODEL_DICT
9+
from model_registry import ModelRegistry as ModelRegistryClient
10+
11+
LOGGER = get_logger(name=__name__)
12+
MINVER = Version.parse(version="2.21.0")
13+
14+
15+
@pytest.mark.parametrize(
16+
"updated_dsc_component_state_scope_class, model_registry_client",
17+
[
18+
pytest.param(
19+
{
20+
"component_patch": {
21+
DscComponents.MODELREGISTRY: {
22+
"managementState": DscComponents.ManagementState.MANAGED,
23+
"registriesNamespace": py_config["model_registry_namespace"],
24+
},
25+
},
26+
},
27+
{"service_fixture": "model_registry_instance_oauth_service"},
28+
id="oauth_proxy",
29+
),
30+
],
31+
indirect=True,
32+
)
33+
@pytest.mark.usefixtures("updated_dsc_component_state_scope_class")
34+
class TestModelRegistryCreationOAuth:
35+
"""
36+
Tests the creation of a model registry with OAuth proxy configuration.
37+
Jira ID: RHOAIENG-26194
38+
"""
39+
40+
@pytest.mark.smoke
41+
def test_registering_model_with_oauth(
42+
self: Self,
43+
admin_client,
44+
model_registry_client: ModelRegistryClient,
45+
):
46+
if py_config["distribution"] == "downstream" and get_product_version(admin_client=admin_client) < MINVER:
47+
pytest.skip("Skipping test for RHOAI < 2.21")
48+
49+
# Register a new model
50+
registered_model = model_registry_client.register_model(
51+
name=MODEL_DICT["model_name"],
52+
uri=MODEL_DICT["model_uri"],
53+
version=MODEL_DICT["model_version"],
54+
description=MODEL_DICT["model_description"],
55+
model_format_name=MODEL_DICT["model_format"],
56+
model_format_version=MODEL_DICT["model_format_version"],
57+
storage_key=MODEL_DICT["model_storage_key"],
58+
storage_path=MODEL_DICT["model_storage_path"],
59+
metadata=MODEL_DICT["model_metadata"],
60+
)
61+
62+
# Get and verify the model
63+
model = model_registry_client.get_registered_model(name=MODEL_NAME)
64+
expected_attrs = {
65+
"id": registered_model.id,
66+
"name": registered_model.name,
67+
"description": registered_model.description,
68+
"owner": registered_model.owner,
69+
"state": registered_model.state,
70+
}
71+
errors = [
72+
f"Unexpected {attr} expected: {expected}, received {getattr(model, attr)}"
73+
for attr, expected in expected_attrs.items()
74+
if getattr(model, attr) != expected
75+
]
76+
if errors:
77+
pytest.fail("errors found in model registry response validation:\n{}".format("\n".join(errors)))

tests/model_registry/rbac/test_mr_rbac.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from utilities.infra import switch_user_context
1010
from kubernetes.dynamic import DynamicClient
1111
from ocp_resources.group import Group
12-
from ocp_resources.model_registry import ModelRegistry
12+
from ocp_resources.model_registry_modelregistry_opendatahub_io import ModelRegistry
1313
from ocp_resources.role import Role
1414
from ocp_resources.role_binding import RoleBinding
1515
from utilities.constants import DscComponents

tests/model_registry/rbac/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from ocp_resources.namespace import Namespace
55
from model_registry import ModelRegistry as ModelRegistryClient
66
from tests.model_registry.utils import get_endpoint_from_mr_service, get_mr_service_by_label
7-
from ocp_resources.model_registry import ModelRegistry
7+
from ocp_resources.model_registry_modelregistry_opendatahub_io import ModelRegistry
88
from utilities.infra import get_openshift_token
99
from kubernetes.dynamic import DynamicClient
1010

0 commit comments

Comments
 (0)