Skip to content

Commit ec76df2

Browse files
nightblureivanalexanderankin
authored
fix: Container for Milvus database (#606)
I use this wonderful package for writing tests, but I did not find a container for [Milvus vector database](https://milvus.io/docs) Please check, I'm ready to correct comments --------- Co-authored-by: ivan <[email protected]> Co-authored-by: David Ankin <[email protected]>
1 parent f5a019b commit ec76df2

File tree

6 files changed

+388
-6
lines changed

6 files changed

+388
-6
lines changed

index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ testcontainers-python facilitates the use of Docker containers for functional an
2828
modules/keycloak/README
2929
modules/localstack/README
3030
modules/memcached/README
31+
modules/milvus/README
3132
modules/minio/README
3233
modules/mongodb/README
3334
modules/mssql/README

modules/milvus/README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. autoclass:: testcontainers.milvus.MilvusContainer
2+
.. title:: testcontainers.milvus.MilvusContainer
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
14+
import requests
15+
16+
from testcontainers.core.config import testcontainers_config as c
17+
from testcontainers.core.generic import DockerContainer
18+
from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
19+
20+
21+
class MilvusContainer(DockerContainer):
22+
"""
23+
Milvus database container.
24+
25+
Read mode about Milvus: https://milvus.io/docs
26+
27+
Example:
28+
29+
The example spins up a Milvus database and connects to it client using MilvisClient.
30+
31+
.. doctest::
32+
33+
>>> from testcontainers.milvus import MilvusContainer
34+
>>> with MilvusContainer("milvusdb/milvus:v2.4.4") as milvus_container:
35+
... milvus_container.get_exposed_port(milvus_container.port) in milvus_container.get_connection_url()
36+
True
37+
"""
38+
39+
def __init__(
40+
self,
41+
image: str = "milvusdb/milvus:latest",
42+
port: int = 19530,
43+
**kwargs,
44+
) -> None:
45+
super().__init__(image=image, **kwargs)
46+
self.port = port
47+
self.healthcheck_port = 9091
48+
self.with_exposed_ports(self.port, self.healthcheck_port)
49+
self.cmd = "milvus run standalone"
50+
51+
envs = {"ETCD_USE_EMBED": "true", "ETCD_DATA_DIR": "/var/lib/milvus/etcd", "COMMON_STORAGETYPE": "local"}
52+
53+
for env, value in envs.items():
54+
self.with_env(env, value)
55+
56+
def get_connection_url(self) -> str:
57+
ip = self.get_container_host_ip()
58+
port = self.get_exposed_port(self.port)
59+
return f"http://{ip}:{port}"
60+
61+
@wait_container_is_ready()
62+
def _connect(self) -> None:
63+
msg = "Welcome to use Milvus!"
64+
wait_for_logs(self, f".*{msg}.*", c.max_tries, c.sleep_time)
65+
self._healthcheck()
66+
67+
def _get_healthcheck_url(self) -> str:
68+
ip = self.get_container_host_ip()
69+
port = self.get_exposed_port(self.healthcheck_port)
70+
return f"http://{ip}:{port}"
71+
72+
@wait_container_is_ready(requests.exceptions.HTTPError)
73+
def _healthcheck(self) -> None:
74+
healthcheck_url = self._get_healthcheck_url()
75+
response = requests.get(f"{healthcheck_url}/healthz", timeout=1)
76+
response.raise_for_status()
77+
78+
def start(self) -> "MilvusContainer":
79+
"""This method starts the Milvus container and runs the healthcheck
80+
to verify that the container is ready to use."""
81+
self.with_command(self.cmd)
82+
super().start()
83+
self._connect()
84+
self._healthcheck()
85+
return self

modules/milvus/tests/test_milvus.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import pytest
2+
from pymilvus import MilvusClient
3+
4+
from testcontainers.milvus import MilvusContainer
5+
6+
VERSIONS = ["v2.4.0", "v2.4.4"]
7+
8+
9+
class ClientMilvusContainer(MilvusContainer):
10+
def get_client(self, *, dbname: str = "default", token: str = "root:Milvus") -> MilvusClient:
11+
connection_url = self.get_connection_url()
12+
client = MilvusClient(uri=connection_url, dbname=dbname, token=token)
13+
return client
14+
15+
16+
@pytest.mark.parametrize("version", VERSIONS)
17+
def test_run_milvus_success(version: str):
18+
image = f"milvusdb/milvus:{version}"
19+
20+
with MilvusContainer(image=image) as milvus_container:
21+
exposed_port = milvus_container.get_exposed_port(milvus_container.port)
22+
url = milvus_container.get_connection_url()
23+
24+
assert url and exposed_port in url
25+
26+
27+
@pytest.mark.parametrize("version", VERSIONS)
28+
def test_milvus_client_success(version: str):
29+
image = f"milvusdb/milvus:{version}"
30+
test_collection = "test_collection"
31+
32+
with ClientMilvusContainer(image=image) as milvus_container:
33+
client = milvus_container.get_client()
34+
client.create_collection(test_collection, dimension=2)
35+
collections = client.list_collections()
36+
assert test_collection in collections
37+
38+
client.drop_collection(test_collection)
39+
assert not client.has_collection(test_collection)

0 commit comments

Comments
 (0)