Skip to content

Commit e91d07e

Browse files
authored
Merge branch 'main' into custom_default_ns
2 parents 25e172e + dcff182 commit e91d07e

38 files changed

+1776
-1639
lines changed

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ markers =
66
# General
77
polarion: Store polarion test ID
88
jira: Store jira bug ID
9+
skip_on_disconnected: Mark tests that can only be run in deployments with Internet access i.e. not on disconnected clusters.
910

1011
# CI
1112
smoke: Mark tests as smoke tests; covers core functionality of the product. Aims to ensure that the build is stable enough for further testing.

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161

6262
LOGGER = get_logger(name=__name__)
6363

64+
pytest_plugins = ["tests.fixtures.inference", "tests.fixtures.guardrails", "tests.fixtures.trustyai"]
65+
6466

6567
@pytest.fixture(scope="session")
6668
def admin_client() -> DynamicClient:

tests/fixtures/guardrails.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from typing import Generator, Any
2+
3+
import pytest
4+
from _pytest.fixtures import FixtureRequest
5+
from kubernetes.dynamic import DynamicClient
6+
from ocp_resources.config_map import ConfigMap
7+
from ocp_resources.deployment import Deployment
8+
from ocp_resources.guardrails_orchestrator import GuardrailsOrchestrator
9+
from ocp_resources.namespace import Namespace
10+
from ocp_resources.pod import Pod
11+
from ocp_resources.resource import ResourceEditor
12+
from ocp_resources.route import Route
13+
14+
from utilities.constants import Labels, Annotations
15+
16+
GUARDRAILS_ORCHESTRATOR_NAME: str = "guardrails-orchestrator"
17+
18+
19+
@pytest.fixture(scope="class")
20+
def guardrails_orchestrator(
21+
request: FixtureRequest,
22+
admin_client: DynamicClient,
23+
model_namespace: Namespace,
24+
orchestrator_config: ConfigMap,
25+
) -> Generator[GuardrailsOrchestrator, Any, Any]:
26+
gorch_kwargs = {
27+
"client": admin_client,
28+
"name": GUARDRAILS_ORCHESTRATOR_NAME,
29+
"namespace": model_namespace.name,
30+
"orchestrator_config": orchestrator_config.name,
31+
"replicas": 1,
32+
"wait_for_resource": True,
33+
}
34+
35+
if enable_built_in_detectors := request.param.get("enable_built_in_detectors"):
36+
gorch_kwargs["enable_built_in_detectors"] = enable_built_in_detectors
37+
38+
if request.param.get("enable_guardrails_gateway"):
39+
guardrails_gateway_config = request.getfixturevalue(argname="guardrails_gateway_config")
40+
gorch_kwargs["enable_guardrails_gateway"] = True
41+
gorch_kwargs["guardrails_gateway_config"] = guardrails_gateway_config.name
42+
43+
with GuardrailsOrchestrator(**gorch_kwargs) as gorch:
44+
gorch_deployment = Deployment(name=gorch.name, namespace=gorch.namespace, wait_for_resource=True)
45+
gorch_deployment.wait_for_replicas()
46+
yield gorch
47+
48+
49+
@pytest.fixture(scope="class")
50+
def orchestrator_config(
51+
request: FixtureRequest, admin_client: DynamicClient, model_namespace: Namespace
52+
) -> Generator[ConfigMap, Any, Any]:
53+
with ConfigMap(
54+
client=admin_client,
55+
name="fms-orchestr8-config-nlp",
56+
namespace=model_namespace.name,
57+
data=request.param["orchestrator_config_data"],
58+
) as cm:
59+
yield cm
60+
61+
62+
@pytest.fixture(scope="class")
63+
def guardrails_gateway_config(
64+
request: FixtureRequest, admin_client: DynamicClient, model_namespace: Namespace
65+
) -> Generator[ConfigMap, Any, Any]:
66+
with ConfigMap(
67+
client=admin_client,
68+
name="fms-orchestr8-config-gateway",
69+
namespace=model_namespace.name,
70+
label={Labels.Openshift.APP: "fmstack-nlp"},
71+
data=request.param["guardrails_gateway_config_data"],
72+
) as cm:
73+
yield cm
74+
75+
76+
@pytest.fixture(scope="class")
77+
def guardrails_orchestrator_pod(
78+
admin_client: DynamicClient,
79+
model_namespace: Namespace,
80+
guardrails_orchestrator: GuardrailsOrchestrator,
81+
) -> Pod:
82+
return list(
83+
Pod.get(
84+
namespace=model_namespace.name, label_selector=f"app.kubernetes.io/instance={GUARDRAILS_ORCHESTRATOR_NAME}"
85+
)
86+
)[0]
87+
88+
89+
@pytest.fixture(scope="class")
90+
def guardrails_orchestrator_route(
91+
admin_client: DynamicClient,
92+
model_namespace: Namespace,
93+
guardrails_orchestrator: GuardrailsOrchestrator,
94+
) -> Generator[Route, Any, Any]:
95+
guardrails_orchestrator_route = Route(
96+
name=f"{guardrails_orchestrator.name}",
97+
namespace=guardrails_orchestrator.namespace,
98+
wait_for_resource=True,
99+
ensure_exists=True,
100+
)
101+
with ResourceEditor(
102+
patches={
103+
guardrails_orchestrator_route: {
104+
"metadata": {
105+
"annotations": {Annotations.HaproxyRouterOpenshiftIo.TIMEOUT: "10m"},
106+
}
107+
}
108+
}
109+
):
110+
yield guardrails_orchestrator_route
111+
112+
113+
@pytest.fixture(scope="class")
114+
def guardrails_orchestrator_url(
115+
guardrails_orchestrator_route: Route,
116+
) -> str:
117+
return f"https://{guardrails_orchestrator_route.host}"
118+
119+
120+
@pytest.fixture(scope="class")
121+
def guardrails_orchestrator_health_route(
122+
admin_client: DynamicClient,
123+
model_namespace: Namespace,
124+
guardrails_orchestrator: GuardrailsOrchestrator,
125+
) -> Generator[Route, Any, Any]:
126+
guardrails_orchestrator_health_route = Route(
127+
name=f"{guardrails_orchestrator.name}-health",
128+
namespace=guardrails_orchestrator.namespace,
129+
wait_for_resource=True,
130+
ensure_exists=True,
131+
)
132+
with ResourceEditor(
133+
patches={
134+
guardrails_orchestrator_health_route: {
135+
"metadata": {
136+
"annotations": {Annotations.HaproxyRouterOpenshiftIo.TIMEOUT: "10m"},
137+
}
138+
}
139+
}
140+
):
141+
yield guardrails_orchestrator_health_route

tests/fixtures/inference.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from typing import Generator, Any
2+
3+
import pytest
4+
from kubernetes.dynamic import DynamicClient
5+
from ocp_resources.inference_service import InferenceService
6+
from ocp_resources.namespace import Namespace
7+
from ocp_resources.pod import Pod
8+
from ocp_resources.secret import Secret
9+
from ocp_resources.service import Service
10+
from ocp_resources.serving_runtime import ServingRuntime
11+
12+
from utilities.constants import RuntimeTemplates, KServeDeploymentType
13+
from utilities.inference_utils import create_isvc
14+
from utilities.serving_runtime import ServingRuntimeFromTemplate
15+
16+
17+
@pytest.fixture(scope="class")
18+
def vllm_cpu_runtime(
19+
admin_client: DynamicClient,
20+
model_namespace: Namespace,
21+
minio_pod: Pod,
22+
minio_service: Service,
23+
minio_data_connection: Secret,
24+
) -> Generator[ServingRuntime, Any, Any]:
25+
with ServingRuntimeFromTemplate(
26+
client=admin_client,
27+
name="vllm-runtime-cpu-fp16",
28+
namespace=model_namespace.name,
29+
template_name=RuntimeTemplates.VLLM_CUDA,
30+
deployment_type=KServeDeploymentType.RAW_DEPLOYMENT,
31+
runtime_image="quay.io/rh-aiservices-bu/vllm-cpu-openai-ubi9"
32+
"@sha256:ada6b3ba98829eb81ae4f89364d9b431c0222671eafb9a04aa16f31628536af2",
33+
containers={
34+
"kserve-container": {
35+
"args": [
36+
"--port=8032",
37+
"--model=/mnt/models",
38+
],
39+
"ports": [{"containerPort": 8032, "protocol": "TCP"}],
40+
"volumeMounts": [{"mountPath": "/dev/shm", "name": "shm"}],
41+
}
42+
},
43+
volumes=[{"emptyDir": {"medium": "Memory", "sizeLimit": "2Gi"}, "name": "shm"}],
44+
) as serving_runtime:
45+
yield serving_runtime
46+
47+
48+
@pytest.fixture(scope="class")
49+
def qwen_isvc(
50+
admin_client: DynamicClient,
51+
model_namespace: Namespace,
52+
minio_pod: Pod,
53+
minio_service: Service,
54+
minio_data_connection: Secret,
55+
vllm_cpu_runtime: ServingRuntime,
56+
) -> Generator[InferenceService, Any, Any]:
57+
with create_isvc(
58+
client=admin_client,
59+
name="qwen-isvc",
60+
namespace=model_namespace.name,
61+
deployment_mode=KServeDeploymentType.RAW_DEPLOYMENT,
62+
model_format="vLLM",
63+
runtime=vllm_cpu_runtime.name,
64+
storage_key=minio_data_connection.name,
65+
storage_path="Qwen2.5-0.5B-Instruct",
66+
wait_for_predictor_pods=False,
67+
resources={
68+
"requests": {"cpu": "2", "memory": "10Gi"},
69+
"limits": {"cpu": "2", "memory": "12Gi"},
70+
},
71+
) as isvc:
72+
yield isvc
73+
74+
75+
@pytest.fixture(scope="class")
76+
def qwen_isvc_url(qwen_isvc: InferenceService) -> str:
77+
return f"http://{qwen_isvc.name}-predictor.{qwen_isvc.namespace}.svc.cluster.local:8032/v1"

tests/fixtures/trustyai.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import pytest
2+
from kubernetes.dynamic import DynamicClient
3+
from ocp_resources.deployment import Deployment
4+
5+
from typing import Generator, Any
6+
7+
from ocp_resources.config_map import ConfigMap
8+
from ocp_resources.resource import ResourceEditor
9+
from pytest_testconfig import py_config
10+
11+
from utilities.constants import Annotations, TRUSTYAI_SERVICE_NAME
12+
13+
14+
@pytest.fixture(scope="class")
15+
def trustyai_operator_deployment(admin_client: DynamicClient) -> Deployment:
16+
return Deployment(
17+
client=admin_client,
18+
name=f"{TRUSTYAI_SERVICE_NAME}-operator-controller-manager",
19+
namespace=py_config["applications_namespace"],
20+
ensure_exists=True,
21+
)
22+
23+
24+
@pytest.fixture(scope="function")
25+
def patched_trustyai_configmap_allow_online(
26+
admin_client: DynamicClient, trustyai_operator_deployment: Deployment
27+
) -> Generator[ConfigMap, Any, Any]:
28+
"""
29+
Patches the TrustyAI Operator ConfigMap in order to set allowOnline and allowCodeExecution to true.
30+
These options are needed to run some LMEval tasks, which rely on having access to the internet
31+
and running arbitrary code. The deployment needs to be restarted in order for these changes to be applied.
32+
"""
33+
trustyai_service_operator: str = "trustyai-service-operator"
34+
35+
configmap: ConfigMap = ConfigMap(
36+
client=admin_client,
37+
name=f"{trustyai_service_operator}-config",
38+
namespace=py_config["applications_namespace"],
39+
ensure_exists=True,
40+
)
41+
with ResourceEditor(
42+
patches={
43+
configmap: {
44+
"metadata": {"annotations": {Annotations.OpenDataHubIo.MANAGED: "false"}},
45+
"data": {
46+
"lmes-allow-online": "true",
47+
"lmes-allow-code-execution": "true",
48+
},
49+
}
50+
}
51+
):
52+
num_replicas: int = trustyai_operator_deployment.replicas
53+
trustyai_operator_deployment.scale_replicas(replica_count=0)
54+
trustyai_operator_deployment.scale_replicas(replica_count=num_replicas)
55+
trustyai_operator_deployment.wait_for_replicas()
56+
yield configmap

0 commit comments

Comments
 (0)