Skip to content

Commit d64cd17

Browse files
committed
ci: resolve conflicts
2 parents 7b07a0a + 38fc0de commit d64cd17

8 files changed

Lines changed: 224 additions & 11 deletions

File tree

conftest.py

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@
66
import shutil
77

88
import shortuuid
9-
from pytest import Parser, Session, FixtureRequest, FixtureDef, Item, Config, CollectReport
9+
from pytest import (
10+
Parser,
11+
Session,
12+
FixtureRequest,
13+
FixtureDef,
14+
Item,
15+
Config,
16+
CollectReport,
17+
)
1018
from _pytest.terminal import TerminalReporter
1119
from typing import Optional, Any
1220
from pytest_testconfig import config as py_config
@@ -39,18 +47,26 @@ def pytest_addoption(parser: Parser) -> None:
3947

4048
# Buckets options
4149
buckets_group.addoption(
42-
"--ci-s3-bucket-name", default=os.environ.get("CI_S3_BUCKET_NAME"), help="Ci S3 bucket name"
50+
"--ci-s3-bucket-name",
51+
default=os.environ.get("CI_S3_BUCKET_NAME"),
52+
help="Ci S3 bucket name",
4353
)
4454
buckets_group.addoption(
45-
"--ci-s3-bucket-region", default=os.environ.get("CI_S3_BUCKET_REGION"), help="Ci S3 bucket region"
55+
"--ci-s3-bucket-region",
56+
default=os.environ.get("CI_S3_BUCKET_REGION"),
57+
help="Ci S3 bucket region",
4658
)
4759

4860
buckets_group.addoption(
49-
"--ci-s3-bucket-endpoint", default=os.environ.get("CI_S3_BUCKET_ENDPOINT"), help="Ci S3 bucket endpoint"
61+
"--ci-s3-bucket-endpoint",
62+
default=os.environ.get("CI_S3_BUCKET_ENDPOINT"),
63+
help="Ci S3 bucket endpoint",
5064
)
5165

5266
buckets_group.addoption(
53-
"--models-s3-bucket-name", default=os.environ.get("MODELS_S3_BUCKET_NAME"), help="Models S3 bucket name"
67+
"--models-s3-bucket-name",
68+
default=os.environ.get("MODELS_S3_BUCKET_NAME"),
69+
help="Models S3 bucket name",
5470
)
5571
buckets_group.addoption(
5672
"--models-s3-bucket-region",
@@ -91,6 +107,11 @@ def pytest_addoption(parser: Parser) -> None:
91107
action="store_true",
92108
help="Delete pre-upgrade resources; useful when debugging pre-upgrade tests",
93109
)
110+
upgrade_group.addoption(
111+
"--upgrade-deployment-modes",
112+
help="Coma-separated str; specify inference service deployment modes tests to run in upgrade tests. "
113+
"If not set, all will be tested.",
114+
)
94115

95116

96117
def pytest_cmdline_main(config: Any) -> None:
@@ -102,19 +123,45 @@ def pytest_collection_modifyitems(session: Session, config: Config, items: list[
102123
Pytest fixture to filter or re-order the items in-place.
103124
104125
Filters upgrade tests based on '--pre-upgrade' / '--post-upgrade' option and marker.
126+
If `--upgrade-deployment-modes` option is set, only tests with the specified deployment modes will be added.
105127
"""
128+
129+
def _add_upgrade_test(_item: Item, _upgrade_deployment_modes: list[str]) -> bool:
130+
"""
131+
Add upgrade test to the list of tests to run.
132+
133+
Args:
134+
_item (Item): The test item.
135+
_upgrade_deployment_modes (list[str]): The deployment modes to test.
136+
137+
Returns:
138+
True if the test should be added, False otherwise.
139+
140+
"""
141+
if not _upgrade_deployment_modes:
142+
return True
143+
144+
return any([keyword for keyword in _item.keywords if keyword in _upgrade_deployment_modes])
145+
106146
pre_upgrade_tests: list[Item] = []
107147
post_upgrade_tests: list[Item] = []
108148
non_upgrade_tests: list[Item] = []
149+
upgrade_deployment_modes: list[str] = []
109150

110151
run_pre_upgrade_tests: str | None = config.getoption(name="pre_upgrade")
111152
run_post_upgrade_tests: str | None = config.getoption(name="post_upgrade")
153+
if config_upgrade_deployment_modes := config.getoption(name="upgrade_deployment_modes"):
154+
upgrade_deployment_modes = config_upgrade_deployment_modes.split(",")
112155

113156
for item in items:
114-
if "pre_upgrade" in item.keywords:
157+
if "pre_upgrade" in item.keywords and _add_upgrade_test(
158+
_item=item, _upgrade_deployment_modes=upgrade_deployment_modes
159+
):
115160
pre_upgrade_tests.append(item)
116161

117-
elif "post_upgrade" in item.keywords:
162+
elif "post_upgrade" in item.keywords and _add_upgrade_test(
163+
_item=item, _upgrade_deployment_modes=upgrade_deployment_modes
164+
):
118165
post_upgrade_tests.append(item)
119166

120167
else:

tests/model_serving/model_server/serverless/conftest.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@
1212
from utilities.manifests.caikit_tgis import CAIKIT_TGIS_INFERENCE_CONFIG
1313

1414

15+
@pytest.fixture(scope="class")
16+
def inference_service_patched_replicas(
17+
request: FixtureRequest, ovms_serverless_inference_service: InferenceService
18+
) -> InferenceService:
19+
ResourceEditor(
20+
patches={
21+
ovms_serverless_inference_service: {
22+
"spec": {
23+
"predictor": {"minReplicas": request.param["min-replicas"]},
24+
}
25+
}
26+
}
27+
).update()
28+
29+
return ovms_serverless_inference_service
30+
31+
1532
@pytest.fixture
1633
def inference_service_updated_canary_config(
1734
request: FixtureRequest, s3_models_inference_service: InferenceService
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import pytest
2+
from ocp_resources.deployment import Deployment
3+
4+
from tests.model_serving.model_server.serverless.utils import verify_no_inference_pods
5+
from tests.model_serving.model_server.utils import verify_inference_response
6+
from utilities.constants import (
7+
ModelFormat,
8+
ModelInferenceRuntime,
9+
ModelVersion,
10+
Protocols,
11+
)
12+
from utilities.exceptions import DeploymentValidationError
13+
from utilities.inference_utils import Inference
14+
from utilities.manifests.onnx import ONNX_INFERENCE_CONFIG
15+
16+
pytestmark = [
17+
pytest.mark.serverless,
18+
pytest.mark.sanity,
19+
pytest.mark.usefixtures("valid_aws_config"),
20+
]
21+
22+
23+
@pytest.mark.serverless
24+
@pytest.mark.parametrize(
25+
"model_namespace, openvino_kserve_serving_runtime, ovms_serverless_inference_service",
26+
[
27+
pytest.param(
28+
{"name": "serverless-scale-zero"},
29+
{
30+
"runtime-name": ModelInferenceRuntime.ONNX_RUNTIME,
31+
"model-format": {ModelFormat.ONNX: ModelVersion.OPSET13},
32+
},
33+
{
34+
"name": ModelFormat.ONNX,
35+
"model-version": ModelVersion.OPSET13,
36+
"model-dir": "test-dir",
37+
},
38+
)
39+
],
40+
indirect=True,
41+
)
42+
class TestServerlessScaleToZero:
43+
def test_serverless_before_scale_to_zero(self, ovms_serverless_inference_service):
44+
"""Verify model can be queried before scaling to zero"""
45+
verify_inference_response(
46+
inference_service=ovms_serverless_inference_service,
47+
inference_config=ONNX_INFERENCE_CONFIG,
48+
inference_type=Inference.INFER,
49+
protocol=Protocols.HTTPS,
50+
use_default_query=True,
51+
)
52+
53+
@pytest.mark.parametrize(
54+
"inference_service_patched_replicas",
55+
[pytest.param({"min-replicas": 0})],
56+
indirect=True,
57+
)
58+
@pytest.mark.dependency(name="test_no_serverless_pods_after_scale_to_zero")
59+
def test_no_serverless_pods_after_scale_to_zero(self, admin_client, inference_service_patched_replicas):
60+
"""Verify pods are scaled to zero"""
61+
verify_no_inference_pods(client=admin_client, isvc=inference_service_patched_replicas)
62+
63+
@pytest.mark.dependency(depends=["test_no_serverless_pods_after_scale_to_zero"])
64+
def test_serverless_inference_after_scale_to_zero(self, ovms_serverless_inference_service):
65+
"""Verify model can be queried after scaling to zero"""
66+
verify_inference_response(
67+
inference_service=ovms_serverless_inference_service,
68+
inference_config=ONNX_INFERENCE_CONFIG,
69+
inference_type=Inference.INFER,
70+
protocol=Protocols.HTTPS,
71+
use_default_query=True,
72+
)
73+
74+
@pytest.mark.dependency(depends=["test_no_serverless_pods_after_scale_to_zero"])
75+
def test_no_serverless_pods_when_no_traffic(self, admin_client, ovms_serverless_inference_service):
76+
"""Verify pods are scaled to zero when no traffic is sent"""
77+
verify_no_inference_pods(client=admin_client, isvc=ovms_serverless_inference_service)
78+
79+
@pytest.mark.parametrize(
80+
"inference_service_patched_replicas",
81+
[pytest.param({"min-replicas": 1})],
82+
indirect=True,
83+
)
84+
def test_serverless_pods_after_scale_to_one_replica(self, admin_client, inference_service_patched_replicas):
85+
"""Verify pod is running after scaling to 1 replica"""
86+
for deployment in Deployment.get(
87+
client=admin_client,
88+
namespace=inference_service_patched_replicas.namespace,
89+
):
90+
if deployment.labels["serving.knative.dev/configurationGeneration"] == "3":
91+
deployment.wait_for_replicas()
92+
return
93+
94+
raise DeploymentValidationError(
95+
f"Inference Service {inference_service_patched_replicas.name} new deployment not found"
96+
)

tests/model_serving/model_server/serverless/utils.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,48 @@
55
from kubernetes.dynamic import DynamicClient
66
from ocp_resources.inference_service import InferenceService
77
from simple_logger.logger import get_logger
8-
from timeout_sampler import TimeoutExpiredError, TimeoutSampler
8+
from timeout_sampler import TimeoutSampler
9+
from timeout_sampler import TimeoutExpiredError
910

1011
from tests.model_serving.model_server.utils import verify_inference_response
1112
from utilities.constants import Timeout
1213
from utilities.exceptions import InferenceCanaryTrafficError
1314
from utilities.infra import get_pods_by_isvc_label
1415

16+
1517
LOGGER = get_logger(name=__name__)
1618

1719

20+
def verify_no_inference_pods(client: DynamicClient, isvc: InferenceService) -> None:
21+
"""
22+
Verify that no inference pods are running for the given InferenceService.
23+
24+
Args:
25+
client (DynamicClient): DynamicClient object
26+
isvc (InferenceService): InferenceService object
27+
28+
Raises:
29+
TimeoutError: If pods are exist after the timeout.
30+
31+
"""
32+
pods = []
33+
34+
try:
35+
pods = TimeoutSampler(
36+
wait_timeout=Timeout.TIMEOUT_4MIN,
37+
sleep=5,
38+
func=get_pods_by_isvc_label,
39+
client=client,
40+
isvc=isvc,
41+
)
42+
if not pods:
43+
return
44+
45+
except TimeoutError:
46+
LOGGER.error(f"{[pod.name for pod in pods]} were not deleted")
47+
raise
48+
49+
1850
def wait_for_canary_rollout(isvc: InferenceService, percentage: int, timeout: int = Timeout.TIMEOUT_5MIN) -> None:
1951
"""
2052
Wait for inference service to be updated with canary rollout.

tests/model_serving/model_server/upgrade/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,13 @@ uv run pytest --post-upgrade
3030
```bash
3131
uv run pytest --pre-upgrade --post-upgrade
3232
```
33+
34+
## To run only specific deployment tests, pass --upgrade-deployment-modes with requested mode(s), for example:
35+
36+
```bash
37+
uv run pytest --pre-upgrade --post-upgrade --upgrade-deployment-modes=servelerss
38+
```
39+
40+
```bash
41+
uv run pytest --pre-upgrade --post-upgrade --upgrade-deployment-modes=servelerss,rawdeployment
42+
```

tests/model_serving/model_server/upgrade/test_upgrade.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@
77
from utilities.manifests.onnx import ONNX_INFERENCE_CONFIG
88
from utilities.manifests.openvino import OPENVINO_INFERENCE_CONFIG
99

10-
pytestmark = [pytest.mark.serverless, pytest.mark.rawdeployment, pytest.mark.modelmesh]
11-
1210

1311
# TODO: add auth, external route and grpc tests
1412

1513

1614
@pytest.mark.usefixtures("valid_aws_config", "skipped_teardown_resources")
1715
class TestPreUpgradeModelServer:
1816
@pytest.mark.pre_upgrade
17+
@pytest.mark.serverless
1918
def test_serverless_onnx_pre_upgrade_inference(self, ovms_serverless_inference_service_scope_session):
2019
"""Verify that kserve Serverless ONNX model can be queried using REST before upgrade"""
2120
verify_inference_response(
@@ -27,6 +26,7 @@ def test_serverless_onnx_pre_upgrade_inference(self, ovms_serverless_inference_s
2726
)
2827

2928
@pytest.mark.pre_upgrade
29+
@pytest.mark.rawdeployment
3030
def test_raw_caikit_bge_pre_upgrade_inference(self, caikit_raw_inference_service_scope_session):
3131
"""Test Caikit bge-large-en embedding model inference using internal route before upgrade"""
3232
verify_inference_response(
@@ -39,6 +39,7 @@ def test_raw_caikit_bge_pre_upgrade_inference(self, caikit_raw_inference_service
3939
)
4040

4141
@pytest.mark.pre_upgrade
42+
@pytest.mark.modelmesh
4243
def test_model_mesh_openvino_pre_upgrade_inference(self, openvino_model_mesh_inference_service_scope_session):
4344
"""Test OpenVINO ModelMesh inference with internal route before upgrade"""
4445
verify_inference_response(
@@ -53,6 +54,7 @@ def test_model_mesh_openvino_pre_upgrade_inference(self, openvino_model_mesh_inf
5354
@pytest.mark.usefixtures("reused_resources")
5455
class TestPostUpgradeModelServer:
5556
@pytest.mark.post_upgrade
57+
@pytest.mark.serverless
5658
@pytest.mark.dependency(name="test_serverless_onnx_post_upgrade_inference_service_exists")
5759
def test_serverless_onnx_post_upgrade_inference_service_exists(
5860
self, ovms_serverless_inference_service_scope_session
@@ -61,6 +63,7 @@ def test_serverless_onnx_post_upgrade_inference_service_exists(
6163
assert ovms_serverless_inference_service_scope_session.exists
6264

6365
@pytest.mark.post_upgrade
66+
@pytest.mark.serverless
6467
@pytest.mark.dependency(depends=["test_serverless_onnx_post_upgrade_inference_service_exists"])
6568
def test_serverless_onnx_post_upgrade_inference(self, ovms_serverless_inference_service_scope_session):
6669
"""Verify that kserve Serverless ONNX model can be queried using REST after upgrade"""
@@ -73,12 +76,14 @@ def test_serverless_onnx_post_upgrade_inference(self, ovms_serverless_inference_
7376
)
7477

7578
@pytest.mark.post_upgrade
79+
@pytest.mark.rawdeployment
7680
@pytest.mark.dependency(name="test_raw_caikit_bge_post_upgrade_inference_exists")
7781
def test_raw_caikit_bge_post_upgrade_inference_exists(self, caikit_raw_inference_service_scope_session):
7882
"""Test that raw deployment inference service exists after upgrade"""
7983
assert caikit_raw_inference_service_scope_session.exists
8084

8185
@pytest.mark.post_upgrade
86+
@pytest.mark.rawdeployment
8287
@pytest.mark.dependency(depends=["test_raw_caikit_bge_post_upgrade_inference_exists"])
8388
def test_raw_caikit_bge_post_upgrade_inference(self, caikit_raw_inference_service_scope_session):
8489
"""Test Caikit bge-large-en embedding model inference using internal route after upgrade"""
@@ -92,6 +97,7 @@ def test_raw_caikit_bge_post_upgrade_inference(self, caikit_raw_inference_servic
9297
)
9398

9499
@pytest.mark.post_upgrade
100+
@pytest.mark.modelmesh
95101
@pytest.mark.dependency(name="test_model_mesh_openvino_post_upgrade_inference_exists")
96102
def test_model_mesh_openvino_post_upgrade_inference_exists(
97103
self, openvino_model_mesh_inference_service_scope_session
@@ -100,6 +106,7 @@ def test_model_mesh_openvino_post_upgrade_inference_exists(
100106
assert openvino_model_mesh_inference_service_scope_session.exists
101107

102108
@pytest.mark.post_upgrade
109+
@pytest.mark.modelmesh
103110
@pytest.mark.dependency(depends=["test_model_mesh_openvino_post_upgrade_inference_exists"])
104111
def test_model_mesh_openvino_post_upgrade_inference(self, openvino_model_mesh_inference_service_scope_session):
105112
"""Test OpenVINO ModelMesh inference with internal route after upgrade"""

utilities/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,9 @@ def __str__(self) -> str:
7070
return f"The {self.type} is not supported"
7171

7272

73+
class DeploymentValidationError(Exception):
74+
pass
75+
76+
7377
class InferenceCanaryTrafficError(Exception):
7478
pass

utilities/infra.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def create_ns(
9191
def wait_for_inference_deployment_replicas(
9292
client: DynamicClient,
9393
isvc: InferenceService,
94-
runtime_name: str | None,
94+
runtime_name: str | None = None,
9595
expected_num_deployments: int = 1,
9696
timeout: int = Timeout.TIMEOUT_5MIN,
9797
) -> list[Deployment]:

0 commit comments

Comments
 (0)