Skip to content

Commit 9b64792

Browse files
authored
Test LMEval and Guardrails orchestrator images (opendatahub-io#350)
* feat: add image validation for trustyai operator and service * feat: add image validation for trustyai operator and service * feat: add fixture for trustyai operator deployment * feat: add fixture for trustyai operator deployment * docs: add docs for service image validation * feat: add lmeval image test * feat: refactor code and add test for guardrails orch * fix: format and copy * fix: method name for validator and remove unnecessary layer calls * fix: add smoke tag and add docstring
1 parent 833706b commit 9b64792

4 files changed

Lines changed: 80 additions & 1 deletion

File tree

tests/model_explainability/guardrails/conftest.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ocp_resources.inference_service import InferenceService
1010
from ocp_resources.namespace import Namespace
1111
from ocp_resources.persistent_volume_claim import PersistentVolumeClaim
12+
from ocp_resources.pod import Pod
1213
from ocp_resources.route import Route
1314
from ocp_resources.secret import Secret
1415
from ocp_resources.service import Service
@@ -25,6 +26,8 @@
2526
from utilities.inference_utils import create_isvc
2627
from utilities.serving_runtime import ServingRuntimeFromTemplate
2728

29+
GORCH_NAME = "gorch-test"
30+
2831
USER_ONE: str = "user-one"
2932
GUARDRAILS_ORCHESTRATOR_PORT: int = 8032
3033

@@ -53,7 +56,7 @@ def guardrails_orchestrator(
5356
) -> Generator[GuardrailsOrchestrator, Any, Any]:
5457
with GuardrailsOrchestrator(
5558
client=admin_client,
56-
name="gorch-test",
59+
name=GORCH_NAME,
5760
namespace=model_namespace.name,
5861
enable_built_in_detectors=True,
5962
enable_guardrails_gateway=True,
@@ -67,6 +70,13 @@ def guardrails_orchestrator(
6770
yield gorch
6871

6972

73+
@pytest.fixture(scope="class")
74+
def guardrails_orchestrator_pod(
75+
admin_client: DynamicClient, model_namespace: Namespace, guardrails_orchestrator: GuardrailsOrchestrator
76+
) -> Pod:
77+
return list(Pod.get(namespace=model_namespace.name, label_selector=f"app.kubernetes.io/instance={GORCH_NAME}"))[0]
78+
79+
7080
@pytest.fixture(scope="class")
7181
def qwen_llm_model(
7282
admin_client: DynamicClient,

tests/model_explainability/guardrails/test_guardrails.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import requests
55
from timeout_sampler import retry
66

7+
from tests.model_explainability.utils import validate_tai_component_images
78
from utilities.constants import Timeout
89

910

@@ -39,3 +40,12 @@ def test_guardrails_info_endpoint(self, admin_client, qwen_llm_model, guardrails
3940
response_data = response.json()
4041
assert response_data["services"]["chat_generation"]["status"] == healthy_status
4142
assert response_data["services"]["regex"]["status"] == healthy_status
43+
44+
@pytest.mark.smoke
45+
def test_validate_guardrails_orchestrator_images(self, guardrails_orchestrator_pod, trustyai_operator_configmap):
46+
"""Test to verify Guardrails pod images.
47+
Checks if the image tag from the ConfigMap is used within the Pod and if it's pinned using a sha256 digest.
48+
"""
49+
validate_tai_component_images(
50+
pod=guardrails_orchestrator_pod, tai_operator_configmap=trustyai_operator_configmap
51+
)

tests/model_explainability/lm_eval/test_lm_eval.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22

3+
from tests.model_explainability.utils import validate_tai_component_images
34
from utilities.constants import Timeout
45

56
LMEVALJOB_COMPLETE_STATE: str = "Complete"
@@ -160,3 +161,27 @@ def test_lmeval_s3_storage(
160161
lmevaljob_s3_offline_pod.wait_for_status(
161162
status=lmevaljob_s3_offline_pod.Status.SUCCEEDED, timeout=Timeout.TIMEOUT_20MIN
162163
)
164+
165+
166+
@pytest.mark.parametrize(
167+
"model_namespace, minio_data_connection",
168+
[
169+
pytest.param(
170+
{"name": "test-lmeval-images"},
171+
{"bucket": "models"},
172+
)
173+
],
174+
indirect=True,
175+
)
176+
@pytest.mark.smoke
177+
def test_verify_lmeval_pod_images(lmevaljob_s3_offline_pod, trustyai_operator_configmap) -> None:
178+
"""Test to verify LMEval pod images.
179+
Checks if the image tag from the ConfigMap is used within the Pod and if it's pinned using a sha256 digest.
180+
181+
Verifies:
182+
- lmeval driver image
183+
- lmeval job runner image
184+
"""
185+
validate_tai_component_images(
186+
pod=lmevaljob_s3_offline_pod, tai_operator_configmap=trustyai_operator_configmap, include_init_containers=True
187+
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import re
2+
from ocp_resources.config_map import ConfigMap
3+
from ocp_resources.pod import Pod
4+
5+
from utilities.general import SHA256_DIGEST_PATTERN
6+
7+
8+
def validate_tai_component_images(
9+
pod: Pod, tai_operator_configmap: ConfigMap, include_init_containers: bool = False
10+
) -> None:
11+
"""Validate pod image against tai configmap images and check image for sha256 digest.
12+
13+
Args:
14+
pod: Pod
15+
tai_operator_configmap: ConfigMap
16+
include_init_containers: bool
17+
18+
Returns:
19+
None
20+
21+
Raises:
22+
AssertionError: If validation fails.
23+
"""
24+
tai_configmap_values = tai_operator_configmap.instance.data.values()
25+
containers = list(pod.instance.spec.containers)
26+
if include_init_containers:
27+
containers.extend(pod.instance.spec.initContainers)
28+
for container in containers:
29+
assert re.search(SHA256_DIGEST_PATTERN, container.image), (
30+
f"{container.name} : {container.image} does not have a valid SHA256 digest."
31+
)
32+
assert container.image in tai_configmap_values, (
33+
f"{container.name} : {container.image} not present in TrustyAI operator configmap."
34+
)

0 commit comments

Comments
 (0)