Skip to content

Commit 85f43fd

Browse files
authored
Added tests to Validated Model Runtimes Component Images (#1148)
* Added tests to Validated Model Runtimes Component Images * Fixed failures
1 parent 6e1e3f0 commit 85f43fd

4 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Serving runtime image validation tests."""
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Fixtures for serving runtime image validation tests.
3+
4+
Creates minimal ServingRuntime + InferenceService so that deployments/pods
5+
are created and their spec.containers[*].image can be validated against
6+
the CSV relatedImages (registry.redhat.io, sha256 digest).
7+
"""
8+
9+
from collections.abc import Generator
10+
from typing import Any
11+
12+
import pytest
13+
from kubernetes.dynamic import DynamicClient
14+
from ocp_resources.namespace import Namespace
15+
from ocp_resources.pod import Pod
16+
from timeout_sampler import TimeoutSampler
17+
18+
from tests.model_serving.model_runtime.image_validation.constant import PLACEHOLDER_STORAGE_URI
19+
from utilities.constants import KServeDeploymentType
20+
from utilities.inference_utils import create_isvc
21+
from utilities.infra import create_ns, wait_for_isvc_pods
22+
from utilities.serving_runtime import ServingRuntimeFromTemplate
23+
24+
25+
@pytest.fixture(scope="class")
26+
def serving_runtime_image_validation_namespace(
27+
admin_client: DynamicClient,
28+
) -> Generator[Namespace, Any, Any]:
29+
"""
30+
A dedicated namespace for serving runtime image validation.
31+
32+
Ensures deployments/pods created by the test have a clean namespace
33+
that is torn down after the test.
34+
"""
35+
name = "runtime-verification"
36+
with create_ns(admin_client=admin_client, name=name, teardown=True) as ns:
37+
yield ns
38+
39+
40+
@pytest.fixture(scope="function")
41+
def serving_runtime_pods_for_runtime(
42+
request: pytest.FixtureRequest,
43+
admin_client: DynamicClient,
44+
serving_runtime_image_validation_namespace: Namespace,
45+
) -> Generator[tuple[list[Pod], str], Any, Any]:
46+
"""
47+
For a given runtime config (parametrized), create ServingRuntime + InferenceService,
48+
wait for pods, yield (pods, display_name) for validation. Teardown after test.
49+
"""
50+
config = request.param
51+
display_name = config["name"]
52+
name_slug = display_name.replace("_", "-")
53+
namespace_name = serving_runtime_image_validation_namespace.name
54+
runtime_name = f"{name_slug}-runtime"
55+
isvc_name = f"{name_slug}-isvc"
56+
57+
with ServingRuntimeFromTemplate(
58+
client=admin_client,
59+
name=runtime_name,
60+
namespace=namespace_name,
61+
template_name=config["template"],
62+
deployment_type="raw",
63+
) as serving_runtime:
64+
# Get model format from the runtime for the InferenceService spec.
65+
model_format = serving_runtime.instance.spec.supportedModelFormats[0].name
66+
with create_isvc(
67+
client=admin_client,
68+
name=isvc_name,
69+
namespace=namespace_name,
70+
model_format=model_format,
71+
runtime=runtime_name,
72+
storage_uri=PLACEHOLDER_STORAGE_URI,
73+
deployment_mode=KServeDeploymentType.RAW_DEPLOYMENT,
74+
wait=False,
75+
wait_for_predictor_pods=False,
76+
timeout=120,
77+
teardown=True,
78+
) as isvc:
79+
# Wait for pods to be created (300 seconds timeout)
80+
for pods in TimeoutSampler(
81+
wait_timeout=300,
82+
sleep=5,
83+
func=wait_for_isvc_pods,
84+
client=admin_client,
85+
isvc=isvc,
86+
runtime_name=runtime_name,
87+
):
88+
if pods:
89+
yield (pods, display_name)
90+
return
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""Constants for serving runtime image validation tests."""
2+
3+
from utilities.constants import RuntimeTemplates
4+
5+
# Placeholder storage URI so the controller creates Deployment/Pod with runtime image.
6+
# No actual model or inference is required; pod phase does not need to be Ready.
7+
PLACEHOLDER_STORAGE_URI = "s3://dummy-bucket/dummy/"
8+
9+
# Runtime configs: display name (for "name : passed") and template name.
10+
# For each we create ServingRuntime + InferenceService, wait for pod(s), validate, then teardown.
11+
RUNTIME_CONFIGS = [
12+
{"name": "odh_openvino_model_server_image", "template": RuntimeTemplates.OVMS_KSERVE},
13+
{"name": "odh_vllm_cpu_image", "template": RuntimeTemplates.VLLM_CPU_x86},
14+
{"name": "odh_vllm_gaudi_image", "template": RuntimeTemplates.VLLM_GAUDI},
15+
{"name": "odh_mlserver_image", "template": RuntimeTemplates.MLSERVER},
16+
{"name": "rhaiis_vllm_cuda_image", "template": RuntimeTemplates.VLLM_CUDA},
17+
{"name": "rhaiis_vllm_rocm_image", "template": RuntimeTemplates.VLLM_ROCM},
18+
{"name": "rhaiis_vllm_spyre_image", "template": RuntimeTemplates.VLLM_SPYRE},
19+
]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
Tests to verify that serving runtime component images meet the requirements:
3+
1. Images are hosted in registry.redhat.io
4+
2. Images use sha256 digest instead of tags
5+
3. Images are listed in the CSV's relatedImages section
6+
7+
For each runtime template we create ServingRuntime + InferenceService, wait for pod(s),
8+
then validate the pod's container images against the cluster CSV (relatedImages) at runtime.
9+
No hardcoded image SHAs—validation uses whatever CSV is installed (e.g. rhods-operator.3.3.0).
10+
"""
11+
12+
from typing import Self
13+
14+
import pytest
15+
from ocp_resources.pod import Pod
16+
from simple_logger.logger import get_logger
17+
18+
from tests.model_serving.model_runtime.image_validation.constant import RUNTIME_CONFIGS
19+
from utilities.general import validate_container_images
20+
21+
LOGGER = get_logger(name=__name__)
22+
23+
pytestmark = [
24+
pytest.mark.downstream_only,
25+
pytest.mark.skip_must_gather,
26+
pytest.mark.smoke,
27+
]
28+
29+
30+
@pytest.mark.parametrize("serving_runtime_pods_for_runtime", RUNTIME_CONFIGS, indirect=True)
31+
class TestServingRuntimeImagesPerTemplate:
32+
"""
33+
For each runtime template: create ServingRuntime + InferenceService, wait for pod(s),
34+
validate pod images (registry.redhat.io, sha256, CSV), output runtimename : passed, then teardown.
35+
"""
36+
37+
def test_verify_serving_runtime_pod_images_from_template(
38+
self: Self,
39+
serving_runtime_pods_for_runtime: tuple[list[Pod], str],
40+
related_images_refs: set[str],
41+
) -> None:
42+
"""
43+
For the parametrized runtime: create SR+ISVC from template, validate pod images, report name : passed.
44+
"""
45+
pods, runtime_name = serving_runtime_pods_for_runtime
46+
validation_errors = []
47+
for pod in pods:
48+
LOGGER.info(f"Validating {pod.name} in {pod.namespace}")
49+
validation_errors.extend(
50+
validate_container_images(
51+
pod=pod,
52+
valid_image_refs=related_images_refs,
53+
)
54+
)
55+
56+
if validation_errors:
57+
pytest.fail("\n".join(validation_errors))
58+
LOGGER.info(f"{runtime_name} : passed")

0 commit comments

Comments
 (0)