Skip to content

Commit db67d52

Browse files
feat: add test for TrustyAI database migration (#372)
* feat: add test for TrustyAI database migration * create PVC only TAI Svc. * upload data to PVC * patch CR to enable migration flags and DB + PVC config * wait for migration * patch to use DB only * schedule metric collection request and verify metrics * fix: remove unused fixture * feat: remove unnecessary if statement * fix: correct namespace * Update tests/model_explainability/trustyai_service/service/test_trustyai_service.py Co-authored-by: Adolfo Aguirrezabal <[email protected]> * fix: remove unnecessary variables and patch --------- Co-authored-by: Adolfo Aguirrezabal <[email protected]>
1 parent 379baff commit db67d52

File tree

4 files changed

+127
-3
lines changed

4 files changed

+127
-3
lines changed

tests/model_explainability/trustyai_service/conftest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ def gaussian_credit_model(
291291
minio_service: Service,
292292
minio_data_connection: Secret,
293293
mlserver_runtime: ServingRuntime,
294-
trustyai_service_with_pvc_storage: TrustyAIService,
295294
teardown_resources: bool,
296295
) -> Generator[InferenceService, Any, Any]:
297296
gaussian_credit_model_kwargs = {

tests/model_explainability/trustyai_service/constants.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
TAI_DATA_CONFIG: Dict[str, str] = {"filename": "data.csv", "format": "CSV"}
77
TAI_METRICS_CONFIG: Dict[str, str] = {"schedule": "5s"}
88
TAI_PVC_STORAGE_CONFIG: Dict[str, str] = {"format": "PVC", "folder": "/inputs", "size": "1Gi"}
9-
TAI_DB_STORAGE_CONFIG = {"format": "DATABASE", "size": "1Gi", "databaseConfigurations": "db-credentials"}
9+
TAI_DB_STORAGE_CONFIG: Dict[str, str] = {
10+
"format": "DATABASE",
11+
"size": "1Gi",
12+
"databaseConfigurations": "db-credentials",
13+
}
1014

1115
SKLEARN: str = "sklearn"
1216
MLSERVER: str = "mlserver"
@@ -56,3 +60,16 @@
5660
}
5761

5862
ISVC_GETTER: str = "isvc-getter"
63+
64+
TRUSTYAI_DB_MIGRATION_PATCH: dict[str, Any] = {
65+
"metadata": {"annotations": {"trustyai.opendatahub.io/db-migration": "true"}},
66+
"spec": {
67+
"storage": {
68+
"format": "DATABASE",
69+
"folder": "/inputs",
70+
"size": "1Gi",
71+
"databaseConfigurations": "db-credentials",
72+
},
73+
"data": {"filename": "data.csv", "format": "BEAN"},
74+
},
75+
}

tests/model_explainability/trustyai_service/service/test_trustyai_service.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
from ocp_resources.namespace import Namespace
33
from ocp_resources.trustyai_service import TrustyAIService
44

5-
from tests.model_explainability.trustyai_service.constants import DRIFT_BASE_DATA_PATH
5+
from tests.model_explainability.trustyai_service.constants import (
6+
DRIFT_BASE_DATA_PATH,
7+
TRUSTYAI_DB_MIGRATION_PATCH,
8+
)
69
from tests.model_explainability.trustyai_service.trustyai_service_utils import (
710
send_inferences_and_verify_trustyai_service_registered,
811
verify_upload_data_to_trustyai_service,
@@ -14,6 +17,10 @@
1417
validate_trustyai_service_db_conn_failure,
1518
validate_trustyai_service_images,
1619
)
20+
from tests.model_explainability.trustyai_service.service.utils import (
21+
wait_for_trustyai_db_migration_complete_log,
22+
patch_trustyai_service_cr,
23+
)
1724
from utilities.constants import MinIo
1825
from utilities.manifests.openvino import OPENVINO_KSERVE_INFERENCE_CONFIG
1926

@@ -176,3 +183,71 @@ def test_validate_trustyai_service_image(
176183
label_selector=f"app.kubernetes.io/instance={trustyai_service_with_pvc_storage.name}",
177184
trustyai_operator_configmap=trustyai_operator_configmap,
178185
)
186+
187+
188+
@pytest.mark.parametrize(
189+
"model_namespace, minio_pod, minio_data_connection",
190+
[
191+
pytest.param(
192+
{"name": "validate-trustyai-db-migration"},
193+
MinIo.PodConfig.MODEL_MESH_MINIO_CONFIG,
194+
{"bucket": MinIo.Buckets.MODELMESH_EXAMPLE_MODELS},
195+
)
196+
],
197+
indirect=True,
198+
)
199+
@pytest.mark.usefixtures("minio_pod")
200+
@pytest.mark.smoke
201+
def test_trustyai_service_db_migration(
202+
admin_client,
203+
current_client_token,
204+
mariadb,
205+
trustyai_db_ca_secret,
206+
trustyai_service_with_pvc_storage,
207+
gaussian_credit_model,
208+
) -> None:
209+
"""Verify if TrustyAI DB Migration works as expected.
210+
This test initializes TrustyAI Service with PVC Storage at first with a database on standby but the service is not
211+
configured to use it.
212+
Data is uploaded to the PVC, then the TrustyAI CR is patched to trigger a migration from PVC to DB storage.
213+
config.
214+
Then waits for the migration success entry in the container logs and patches the service again to remove PVC config.
215+
Finally, a metric is scheduled and checked if the service works as expected post migration.
216+
217+
Args:
218+
admin_client: DynamicClient
219+
current_client_token: RedactedString
220+
mariadb: MariaDB
221+
trustyai_db_ca_secret: None
222+
trustyai_service_with_pvc_storage: TrustyAIService
223+
gaussian_credit_model: Generator[InferenceService, Any, Any]
224+
225+
Returns:
226+
None
227+
"""
228+
verify_upload_data_to_trustyai_service(
229+
client=admin_client,
230+
trustyai_service=trustyai_service_with_pvc_storage,
231+
token=current_client_token,
232+
data_path=f"{DRIFT_BASE_DATA_PATH}/training_data.json",
233+
)
234+
235+
trustyai_db_migration_patched_service = patch_trustyai_service_cr(
236+
trustyai_service=trustyai_service_with_pvc_storage, patches=TRUSTYAI_DB_MIGRATION_PATCH
237+
)
238+
239+
wait_for_trustyai_db_migration_complete_log(
240+
client=admin_client,
241+
trustyai_service=trustyai_db_migration_patched_service,
242+
)
243+
244+
verify_trustyai_service_metric_scheduling_request(
245+
client=admin_client,
246+
trustyai_service=trustyai_db_migration_patched_service,
247+
token=current_client_token,
248+
metric_name=TrustyAIServiceMetrics.Drift.MEANSHIFT,
249+
json_data={
250+
"modelId": gaussian_credit_model.name,
251+
"referenceTag": "TRAINING",
252+
},
253+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import re
2+
from typing import Any
3+
4+
from kubernetes.dynamic import DynamicClient
5+
from ocp_resources.pod import Pod
6+
from ocp_resources.resource import ResourceEditor
7+
from ocp_resources.trustyai_service import TrustyAIService
8+
from timeout_sampler import retry
9+
10+
from tests.model_explainability.trustyai_service.trustyai_service_utils import TRUSTYAI_SERVICE_NAME
11+
from utilities.constants import Timeout
12+
13+
14+
@retry(wait_timeout=Timeout.TIMEOUT_5MIN, sleep=5)
15+
def wait_for_trustyai_db_migration_complete_log(client: DynamicClient, trustyai_service: TrustyAIService) -> bool:
16+
trustyai_pod = list(
17+
Pod.get(
18+
dyn_client=client,
19+
namespace=trustyai_service.namespace,
20+
label_selector=f"app.kubernetes.io/instance={trustyai_service.name}",
21+
)
22+
)[0]
23+
return bool(
24+
re.search(
25+
r".+INFO.+Migration complete, the PVC is now safe to remove\.",
26+
trustyai_pod.log(container=TRUSTYAI_SERVICE_NAME),
27+
)
28+
)
29+
30+
31+
def patch_trustyai_service_cr(trustyai_service: TrustyAIService, patches: dict[str, Any]) -> TrustyAIService:
32+
ResourceEditor(patches={trustyai_service: patches}).update()
33+
return trustyai_service

0 commit comments

Comments
 (0)