Skip to content

Commit 6135f71

Browse files
Bobbins228jgarciao
andauthored
test: add qdrant vector_io tests (opendatahub-io#1029)
* test: add qdrant vector_io tests * test: update env vars for qdrant and readme added generic secrets --------- Co-authored-by: Jorge <jgarciao@users.noreply.github.com>
1 parent af60150 commit 6135f71

File tree

4 files changed

+267
-12
lines changed

4 files changed

+267
-12
lines changed

tests/fixtures/vector_io.py

Lines changed: 193 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ocp_resources.deployment import Deployment
88
from ocp_resources.namespace import Namespace
99
from ocp_resources.service import Service
10+
from ocp_resources.secret import Secret
1011

1112

1213
MILVUS_IMAGE = os.getenv(
@@ -30,11 +31,26 @@
3031
PGVECTOR_USER = os.getenv("LLS_VECTOR_IO_PGVECTOR_USER", "vector_user")
3132
PGVECTOR_PASSWORD = os.getenv("LLS_VECTOR_IO_PGVECTOR_PASSWORD", "yourpassword")
3233

34+
# qdrant v1 unprivileged latest
35+
QDRANT_IMAGE = os.getenv(
36+
"LLS_VECTOR_IO_QDRANT_IMAGE",
37+
(
38+
"docker.io/qdrant/qdrant@sha256:"
39+
"9dfabc51ededc48158899a288a19a04de1ab54a11d8c512e1c40eebbd5e2bc92" # pragma: allowlist secret
40+
),
41+
)
42+
43+
QDRANT_API_KEY = os.getenv("LLS_VECTOR_IO_QDRANT_API_KEY", "yourapikey")
44+
QDRANT_URL = os.getenv("LLS_VECTOR_IO_QDRANT_URL", "http://vector-io-qdrant-service:6333")
45+
3346

3447
@pytest.fixture(scope="class")
3548
def vector_io_provider_deployment_config_factory(
49+
unprivileged_client: DynamicClient,
50+
unprivileged_model_namespace: Namespace,
51+
vector_io_secret: Secret,
3652
request: FixtureRequest,
37-
) -> Callable[[str], list[Dict[str, str]]]:
53+
) -> Callable[[str], list[Dict[str, Any]]]:
3854
"""
3955
Factory fixture for deploying vector I/O providers and returning their configuration.
4056
@@ -66,23 +82,35 @@ def vector_io_provider_deployment_config_factory(
6682
* PGVECTOR_USER: Database user
6783
* PGVECTOR_PASSWORD: Database password
6884
* PGVECTOR_DB: Database name
85+
- "qdrant-remote":
86+
* ENABLE_QDRANT: enable qdrant provider
87+
* QDRANT_API_KEY: Qdrant API key
88+
* QDRANT_URL: Qdrant service URL with protocol (e.g., "http://vector-io-qdrant-service:6333")
6989
7090
Example:
7191
def test_with_milvus(vector_io_provider_deployment_config_factory):
7292
env_vars = vector_io_provider_deployment_config_factory("milvus-remote")
7393
# env_vars contains MILVUS_ENDPOINT, MILVUS_TOKEN, etc.
7494
"""
95+
_ = unprivileged_client
96+
_ = unprivileged_model_namespace
97+
_ = vector_io_secret
7598

76-
def _factory(provider_name: str) -> list[Dict[str, str]]:
77-
env_vars: list[dict[str, str]] = []
99+
def _factory(provider_name: str) -> list[Dict[str, Any]]:
100+
env_vars: list[dict[str, Any]] = []
78101

79102
if provider_name is None or provider_name == "milvus":
80103
# Default case - no additional environment variables needed
81104
pass
82105
elif provider_name == "milvus-remote":
83106
request.getfixturevalue(argname="milvus_service")
84107
env_vars.append({"name": "MILVUS_ENDPOINT", "value": "http://vector-io-milvus-service:19530"})
85-
env_vars.append({"name": "MILVUS_TOKEN", "value": MILVUS_TOKEN})
108+
env_vars.append(
109+
{
110+
"name": "MILVUS_TOKEN",
111+
"valueFrom": {"secretKeyRef": {"name": "vector-io-secret", "key": "milvus-token"}},
112+
},
113+
)
86114
env_vars.append({"name": "MILVUS_CONSISTENCY_LEVEL", "value": "Bounded"})
87115
elif provider_name == "faiss":
88116
env_vars.append({"name": "ENABLE_FAISS", "value": "faiss"})
@@ -95,15 +123,54 @@ def _factory(provider_name: str) -> list[Dict[str, str]]:
95123
env_vars.append({"name": "ENABLE_PGVECTOR", "value": "true"})
96124
env_vars.append({"name": "PGVECTOR_HOST", "value": "vector-io-pgvector-service"})
97125
env_vars.append({"name": "PGVECTOR_PORT", "value": "5432"})
98-
env_vars.append({"name": "PGVECTOR_USER", "value": PGVECTOR_USER})
99-
env_vars.append({"name": "PGVECTOR_PASSWORD", "value": PGVECTOR_PASSWORD})
126+
env_vars.append(
127+
{
128+
"name": "PGVECTOR_USER",
129+
"valueFrom": {"secretKeyRef": {"name": "vector-io-secret", "key": "pgvector-user"}},
130+
},
131+
)
132+
env_vars.append(
133+
{
134+
"name": "PGVECTOR_PASSWORD",
135+
"valueFrom": {"secretKeyRef": {"name": "vector-io-secret", "key": "pgvector-password"}},
136+
},
137+
)
100138
env_vars.append({"name": "PGVECTOR_DB", "value": "pgvector"})
139+
elif provider_name == "qdrant-remote":
140+
request.getfixturevalue(argname="qdrant_service")
141+
env_vars.append({"name": "ENABLE_QDRANT", "value": "true"})
142+
env_vars.append({"name": "QDRANT_URL", "value": QDRANT_URL})
143+
env_vars.append({
144+
"name": "QDRANT_API_KEY",
145+
"valueFrom": {"secretKeyRef": {"name": "vector-io-secret", "key": "qdrant-api-key"}},
146+
})
101147

102148
return env_vars
103149

104150
return _factory
105151

106152

153+
@pytest.fixture(scope="class")
154+
def vector_io_secret(
155+
unprivileged_client: DynamicClient,
156+
unprivileged_model_namespace: Namespace,
157+
) -> Generator[Secret, Any, Any]:
158+
"""Create a secret for the vector I/O providers"""
159+
with Secret(
160+
client=unprivileged_client,
161+
namespace=unprivileged_model_namespace.name,
162+
name="vector-io-secret",
163+
type="Opaque",
164+
string_data={
165+
"qdrant-api-key": QDRANT_API_KEY,
166+
"pgvector-user": PGVECTOR_USER,
167+
"pgvector-password": PGVECTOR_PASSWORD,
168+
"milvus-token": MILVUS_TOKEN,
169+
},
170+
) as secret:
171+
yield secret
172+
173+
107174
@pytest.fixture(scope="class")
108175
def etcd_deployment(
109176
unprivileged_client: DynamicClient,
@@ -152,8 +219,13 @@ def remote_milvus_deployment(
152219
unprivileged_model_namespace: Namespace,
153220
etcd_deployment: Deployment,
154221
etcd_service: Service,
222+
vector_io_secret: Secret,
155223
) -> Generator[Deployment, Any, Any]:
156224
"""Deploy a remote Milvus instance for vector I/O provider testing."""
225+
_ = etcd_deployment
226+
_ = etcd_service
227+
_ = vector_io_secret
228+
157229
with Deployment(
158230
client=unprivileged_client,
159231
namespace=unprivileged_model_namespace.name,
@@ -176,6 +248,8 @@ def milvus_service(
176248
remote_milvus_deployment: Deployment,
177249
) -> Generator[Service, Any, Any]:
178250
"""Create a service for the remote Milvus deployment."""
251+
_ = remote_milvus_deployment
252+
179253
with Service(
180254
client=unprivileged_client,
181255
namespace=unprivileged_model_namespace.name,
@@ -272,8 +346,11 @@ def get_etcd_deployment_template() -> Dict[str, Any]:
272346
def pgvector_deployment(
273347
unprivileged_client: DynamicClient,
274348
unprivileged_model_namespace: Namespace,
349+
vector_io_secret: Secret,
275350
) -> Generator[Deployment, Any, Any]:
276351
"""Deploy a PGVector instance for vector I/O provider testing."""
352+
_ = vector_io_secret
353+
277354
with Deployment(
278355
client=unprivileged_client,
279356
namespace=unprivileged_model_namespace.name,
@@ -296,6 +373,8 @@ def pgvector_service(
296373
pgvector_deployment: Deployment,
297374
) -> Generator[Service, Any, Any]:
298375
"""Create a service for the PGVector deployment."""
376+
_ = pgvector_deployment
377+
299378
with Service(
300379
client=unprivileged_client,
301380
namespace=unprivileged_model_namespace.name,
@@ -325,8 +404,14 @@ def get_pgvector_deployment_template() -> Dict[str, Any]:
325404
"ports": [{"containerPort": 5432}],
326405
"env": [
327406
{"name": "POSTGRES_DB", "value": "pgvector"},
328-
{"name": "POSTGRES_USER", "value": PGVECTOR_USER},
329-
{"name": "POSTGRES_PASSWORD", "value": PGVECTOR_PASSWORD},
407+
{
408+
"name": "POSTGRES_USER",
409+
"valueFrom": {"secretKeyRef": {"name": "vector-io-secret", "key": "pgvector-user"}},
410+
},
411+
{
412+
"name": "POSTGRES_PASSWORD",
413+
"valueFrom": {"secretKeyRef": {"name": "vector-io-secret", "key": "pgvector-password"}},
414+
},
330415
{"name": "PGDATA", "value": "/var/lib/postgresql/data/pgdata"},
331416
],
332417
"lifecycle": {
@@ -350,3 +435,103 @@ def get_pgvector_deployment_template() -> Dict[str, Any]:
350435
"volumes": [{"name": "pgdata", "emptyDir": {}}],
351436
},
352437
}
438+
439+
440+
@pytest.fixture(scope="class")
441+
def qdrant_deployment(
442+
unprivileged_client: DynamicClient,
443+
unprivileged_model_namespace: Namespace,
444+
vector_io_secret: Secret,
445+
) -> Generator[Deployment, Any, Any]:
446+
"""Deploy a Qdrant instance for vector I/O provider testing."""
447+
_ = vector_io_secret
448+
449+
with Deployment(
450+
client=unprivileged_client,
451+
namespace=unprivileged_model_namespace.name,
452+
name="vector-io-qdrant-deployment",
453+
min_ready_seconds=5,
454+
replicas=1,
455+
selector={"matchLabels": {"app": "qdrant"}},
456+
strategy={"type": "Recreate"},
457+
template=get_qdrant_deployment_template(),
458+
teardown=True,
459+
) as deployment:
460+
deployment.wait_for_replicas(deployed=True, timeout=240)
461+
yield deployment
462+
463+
464+
@pytest.fixture(scope="class")
465+
def qdrant_service(
466+
unprivileged_client: DynamicClient,
467+
unprivileged_model_namespace: Namespace,
468+
qdrant_deployment: Deployment,
469+
) -> Generator[Service, Any, Any]:
470+
"""Create a service for the Qdrant deployment."""
471+
_ = qdrant_deployment
472+
473+
with Service(
474+
client=unprivileged_client,
475+
namespace=unprivileged_model_namespace.name,
476+
name="vector-io-qdrant-service",
477+
ports=[
478+
{
479+
"name": "http",
480+
"port": 6333,
481+
"targetPort": 6333,
482+
},
483+
{
484+
"name": "grpc",
485+
"port": 6334,
486+
"targetPort": 6334,
487+
},
488+
],
489+
selector={"app": "qdrant"},
490+
wait_for_resource=True,
491+
) as service:
492+
yield service
493+
494+
495+
def get_qdrant_deployment_template() -> Dict[str, Any]:
496+
"""Return a Kubernetes deployment for Qdrant"""
497+
return {
498+
"metadata": {"labels": {"app": "qdrant"}},
499+
"spec": {
500+
"containers": [
501+
{
502+
"name": "qdrant",
503+
"image": QDRANT_IMAGE,
504+
"ports": [
505+
{
506+
"containerPort": 6333,
507+
"name": "http",
508+
},
509+
{
510+
"containerPort": 6334,
511+
"name": "grpc",
512+
},
513+
],
514+
"env": [
515+
{
516+
"name": "QDRANT__SERVICE__API_KEY",
517+
"valueFrom": {
518+
"secretKeyRef": {
519+
"name": "vector-io-secret",
520+
"key": "qdrant-api-key",
521+
},
522+
},
523+
},
524+
],
525+
"volumeMounts": [
526+
{"name": "qdrant-storage", "mountPath": "/qdrant/storage"},
527+
{
528+
"name": "qdrant-storage",
529+
"mountPath": "/qdrant/snapshots",
530+
"subPath": "snapshots",
531+
},
532+
],
533+
},
534+
],
535+
"volumes": [{"name": "qdrant-storage", "emptyDir": {}}],
536+
},
537+
}

tests/llama_stack/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ LLS_VECTOR_IO_ETCD_IMAGE=<CUSTOM-ETCD-IMAGE> # Optional
7171
LLS_VECTOR_IO_PGVECTOR_IMAGE=<CUSTOM-PGVECTOR-IMAGE> # Optional
7272
LLS_VECTOR_IO_PGVECTOR_USER=<CUSTOM-PGVECTOR-USER> # Optional
7373
LLS_VECTOR_IO_PGVECTOR_PASSWORD=<CUSTOM-PGVECTOR-PASSWORD> # Optional
74+
LLS_VECTOR_IO_QDRANT_IMAGE=<CUSTOM-QDRANT-IMAGE> # Optional
75+
LLS_VECTOR_IO_QDRANT_API_KEY=<CUSTOM-QDRANT-API-KEY> # Optional
76+
LLS_VECTOR_IO_QDRANT_URL=<QDRANT_URL_WITH_PROTOCOL> # Optional
7477

7578
# Red Hat Llama Stack Distribution requires PostgreSQL (replacing SQLite)
7679
LLS_VECTOR_IO_POSTGRES_IMAGE=<CUSTOM-POSTGRES-IMAGE> # Optional

0 commit comments

Comments
 (0)