Skip to content

Commit 4aff679

Browse files
fix: Add Cockroach DB Module to Testcontainers (#608)
Adds [Cockroach DB] (https://www.cockroachlabs.com/) module to use with Test containers I had done this previously under #281, but opted to just redo it rather than try to rebase all the things. - [x] Create a new feature directory and populate it with the package structure [described in the documentation](https://testcontainers-python.readthedocs.io/en/latest/#package-structure). Copying one of the existing features is likely the best way to get started. - [x] Implement the new feature (typically in `__init__.py`) and corresponding tests. - [x] Update the feature `README.rst` and add it to the table of contents (`toctree` directive) in the top-level `README.rst`. - [] Add a line `[feature name]` to the list of components in the GitHub Action workflow in `.github/workflows/main.yml` to run tests, build, and publish your package when pushed to the `main` branch. - [x] Rebase your development branch on `main` (or merge `main` into your development branch). - [x] Add Package to pyproject.toml - [ ] Add a line `-e file:[feature name]` to `requirements.in` and open a pull request. Opening a pull request will automatically generate lock files to ensure reproducible builds (see the [pip-tools documentation](https://pip-tools.readthedocs.io/en/latest/) for details). Finally, run `python get_requirements.py --pr=[your PR number]` to fetch the updated requirement files (the build needs to have succeeded). --------- Co-authored-by: joelhess <joelhess@flywheel.io> Co-authored-by: David Ankin <daveankin@gmail.com>
1 parent 0768490 commit 4aff679

File tree

5 files changed

+135
-1
lines changed

5 files changed

+135
-1
lines changed

modules/cockroachdb/README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. autoclass:: testcontainers.cockroachdb.CockroachDBContainer
2+
.. title:: testcontainers.cockroachdb.CockroachDBContainer
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
from os import environ
14+
from typing import Optional
15+
from urllib.error import HTTPError, URLError
16+
from urllib.request import urlopen
17+
18+
from testcontainers.core.generic import DbContainer
19+
from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
20+
21+
22+
class CockroachDBContainer(DbContainer):
23+
"""
24+
CockroachDB database container.
25+
26+
Example:
27+
28+
The example will spin up a CockroachDB database to which you can connect with the credentials
29+
passed in the constructor. Alternatively, you may use the :code:`get_connection_url()`
30+
method which returns a sqlalchemy-compatible url in format
31+
:code:`dialect+driver://username:password@host:port/database`.
32+
33+
.. doctest::
34+
35+
>>> import sqlalchemy
36+
>>> from testcontainers.cockroachdb import CockroachDBContainer
37+
38+
>>> with CockroachDBContainer('cockroachdb/cockroach:v24.1.1') as crdb:
39+
... engine = sqlalchemy.create_engine(crdb.get_connection_url())
40+
... with engine.begin() as connection:
41+
... result = connection.execute(sqlalchemy.text("select version()"))
42+
... version, = result.fetchone()
43+
44+
"""
45+
46+
COCKROACH_DB_PORT: int = 26257
47+
COCKROACH_API_PORT: int = 8080
48+
49+
def __init__(
50+
self,
51+
image: str = "cockroachdb/cockroach:v24.1.1",
52+
username: Optional[str] = None,
53+
password: Optional[str] = None,
54+
dbname: Optional[str] = None,
55+
dialect="cockroachdb+psycopg2",
56+
**kwargs,
57+
) -> None:
58+
super().__init__(image, **kwargs)
59+
60+
self.with_exposed_ports(self.COCKROACH_DB_PORT, self.COCKROACH_API_PORT)
61+
self.username = username or environ.get("COCKROACH_USER", "cockroach")
62+
self.password = password or environ.get("COCKROACH_PASSWORD", "arthropod")
63+
self.dbname = dbname or environ.get("COCKROACH_DATABASE", "roach")
64+
self.dialect = dialect
65+
66+
def _configure(self) -> None:
67+
self.with_env("COCKROACH_DATABASE", self.dbname)
68+
self.with_env("COCKROACH_USER", self.username)
69+
self.with_env("COCKROACH_PASSWORD", self.password)
70+
71+
cmd = "start-single-node"
72+
if not self.password:
73+
cmd += " --insecure"
74+
self.with_command(cmd)
75+
76+
@wait_container_is_ready(HTTPError, URLError)
77+
def _connect(self) -> None:
78+
host = self.get_container_host_ip()
79+
url = f"http://{host}:{self.get_exposed_port(self.COCKROACH_API_PORT)}/health"
80+
self._wait_for_health(url)
81+
wait_for_logs(self, "finished creating default user*")
82+
83+
@staticmethod
84+
def _wait_for_health(url):
85+
with urlopen(url) as response:
86+
response.read()
87+
88+
def get_connection_url(self) -> str:
89+
conn_str = super()._create_connection_url(
90+
dialect=self.dialect,
91+
username=self.username,
92+
password=self.password,
93+
dbname=self.dbname,
94+
port=self.COCKROACH_DB_PORT,
95+
)
96+
97+
if self.password:
98+
conn_str += "?sslmode=require"
99+
100+
return conn_str
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import sqlalchemy
2+
3+
from testcontainers.cockroachdb import CockroachDBContainer
4+
5+
6+
def test_docker_run_mysql():
7+
config = CockroachDBContainer("cockroachdb/cockroach:v24.1.1")
8+
with config as crdb:
9+
engine = sqlalchemy.create_engine(crdb.get_connection_url())
10+
with engine.begin() as connection:
11+
result = connection.execute(sqlalchemy.text("select version()"))
12+
for row in result:
13+
assert "CockroachDB" in row[0]
14+
assert "v24.1.1" in row[0]

poetry.lock

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ packages = [
3333
{ include = "testcontainers", from = "modules/cassandra" },
3434
{ include = "testcontainers", from = "modules/chroma" },
3535
{ include = "testcontainers", from = "modules/clickhouse" },
36+
{ include = "testcontainers", from = "modules/cockroachdb" },
3637
{ include = "testcontainers", from = "modules/elasticsearch" },
3738
{ include = "testcontainers", from = "modules/google" },
3839
{ include = "testcontainers", from = "modules/influxdb" },
@@ -107,6 +108,7 @@ arangodb = ["python-arango"]
107108
azurite = ["azure-storage-blob"]
108109
cassandra = []
109110
clickhouse = ["clickhouse-driver"]
111+
cockroachdb = []
110112
elasticsearch = []
111113
google = ["google-cloud-pubsub", "google-cloud-datastore"]
112114
influxdb = ["influxdb", "influxdb-client"]
@@ -157,6 +159,7 @@ hvac = "2.1.0"
157159
pymilvus = "2.4.3"
158160
httpx = "0.27.0"
159161
paho-mqtt = "2.1.0"
162+
sqlalchemy-cockroachdb = "2.0.2"
160163

161164
[[tool.poetry.source]]
162165
name = "PyPI"

0 commit comments

Comments
 (0)