Skip to content

Commit 1e43923

Browse files
authored
fix: Add Db2 support (#673)
fix #672 Add Db2 support #672.
1 parent 8c28a86 commit 1e43923

File tree

8 files changed

+169
-1
lines changed

8 files changed

+169
-1
lines changed

modules/db2/README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. autoclass:: testcontainers.db2.Db2Container
2+
.. title:: testcontainers.db2.Db2Container
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from os import environ
2+
from typing import Optional
3+
4+
from testcontainers.core.generic import DbContainer
5+
from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
6+
7+
8+
class Db2Container(DbContainer):
9+
"""
10+
IBM Db2 database container.
11+
12+
Example:
13+
14+
.. doctest::
15+
16+
>>> import sqlalchemy
17+
>>> from testcontainers.db2 import Db2Container
18+
19+
>>> with Db2Container("icr.io/db2_community/db2:latest") as db2:
20+
... engine = sqlalchemy.create_engine(db2.get_connection_url())
21+
... with engine.begin() as connection:
22+
... result = connection.execute(sqlalchemy.text("select service_level from sysibmadm.env_inst_info"))
23+
"""
24+
25+
def __init__(
26+
self,
27+
image: str = "icr.io/db2_community/db2:latest",
28+
username: str = "db2inst1",
29+
password: Optional[str] = None,
30+
port: int = 50000,
31+
dbname: str = "testdb",
32+
dialect: str = "db2+ibm_db",
33+
**kwargs,
34+
) -> None:
35+
super().__init__(image, **kwargs)
36+
37+
self.port = port
38+
self.with_exposed_ports(self.port)
39+
40+
self.password = password or environ.get("DB2_PASSWORD", "password")
41+
self.username = username
42+
self.dbname = dbname
43+
self.dialect = dialect
44+
45+
def _configure(self) -> None:
46+
self.with_env("LICENSE", "accept")
47+
self.with_env("DB2INSTANCE", self.username)
48+
self.with_env("DB2INST1_PASSWORD", self.password)
49+
self.with_env("DBNAME", self.dbname)
50+
self.with_env("ARCHIVE_LOGS", "false")
51+
self.with_env("AUTOCONFIG", "false")
52+
self.with_kwargs(privileged=True)
53+
54+
@wait_container_is_ready()
55+
def _connect(self) -> None:
56+
wait_for_logs(self, predicate="Setup has completed")
57+
58+
def get_connection_url(self) -> str:
59+
return super()._create_connection_url(
60+
dialect=self.dialect, username=self.username, password=self.password, dbname=self.dbname, port=self.port
61+
)

modules/db2/tests/test_db2.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from unittest import mock
2+
3+
import pytest
4+
import sqlalchemy
5+
6+
from testcontainers.core.utils import is_arm
7+
from testcontainers.db2 import Db2Container
8+
9+
10+
@pytest.mark.skipif(is_arm(), reason="db2 container not available for ARM")
11+
@pytest.mark.parametrize("version", ["11.5.9.0", "11.5.8.0"])
12+
def test_docker_run_db2(version: str):
13+
with Db2Container(f"icr.io/db2_community/db2:{version}", password="password") as db2:
14+
engine = sqlalchemy.create_engine(db2.get_connection_url())
15+
with engine.begin() as connection:
16+
result = connection.execute(sqlalchemy.text("select service_level from sysibmadm.env_inst_info"))
17+
for row in result:
18+
assert row[0] == f"DB2 v{version}"
19+
20+
21+
# This is a feature in the generic DbContainer class
22+
# but it can't be tested on its own
23+
# so is tested in various database modules:
24+
# - mysql / mariadb
25+
# - postgresql
26+
# - sqlserver
27+
# - mongodb
28+
# - db2
29+
def test_quoted_password():
30+
user = "db2inst1"
31+
dbname = "testdb"
32+
password = "p@$%25+0&%rd :/!=?"
33+
quoted_password = "p%40%24%2525+0%26%25rd %3A%2F%21%3D%3F"
34+
kwargs = {
35+
"username": user,
36+
"password": password,
37+
"dbname": dbname,
38+
}
39+
with Db2Container("icr.io/db2_community/db2:11.5.9.0", **kwargs) as container:
40+
port = container.get_exposed_port(50000)
41+
host = container.get_container_host_ip()
42+
expected_url = f"db2+ibm_db://{user}:{quoted_password}@{host}:{port}/{dbname}"
43+
assert expected_url == container.get_connection_url()

modules/mongodb/tests/test_mongodb.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def test_docker_run_mongodb(version: str):
3535
# - postgresql
3636
# - sqlserver
3737
# - mongodb
38+
# - db2
3839
def test_quoted_password():
3940
user = "root"
4041
password = "p@$%25+0&%rd :/!=?"

modules/mysql/tests/test_mysql.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def test_docker_env_variables():
6969
# - postgresql
7070
# - sqlserver
7171
# - mongodb
72+
# - db2
7273
def test_quoted_password():
7374
user = "root"
7475
password = "p@$%25+0&%rd :/!=?"

modules/postgres/tests/test_postgres.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def test_docker_run_postgres_with_driver_pg8000():
5353
# - postgresql
5454
# - sqlserver
5555
# - mongodb
56+
# - db2
5657
def test_quoted_password():
5758
user = "root"
5859
password = "p@$%25+0&%rd :/!=?"

poetry.lock

Lines changed: 57 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
@@ -36,6 +36,7 @@ packages = [
3636
{ include = "testcontainers", from = "modules/clickhouse" },
3737
{ include = "testcontainers", from = "modules/cockroachdb" },
3838
{ include = "testcontainers", from = "modules/cosmosdb" },
39+
{ include = "testcontainers", from = "modules/db2" },
3940
{ include = "testcontainers", from = "modules/elasticsearch" },
4041
{ include = "testcontainers", from = "modules/generic" },
4142
{ include = "testcontainers", from = "modules/test_module_import"},
@@ -114,6 +115,7 @@ httpx = { version = "*", optional = true }
114115
azure-cosmos = { version = "*", optional = true }
115116
cryptography = { version = "*", optional = true }
116117
trino = { version = "*", optional = true }
118+
ibm_db_sa = { version = "*", optional = true }
117119

118120
[tool.poetry.extras]
119121
arangodb = ["python-arango"]
@@ -123,6 +125,7 @@ cassandra = []
123125
clickhouse = ["clickhouse-driver"]
124126
cosmosdb = ["azure-cosmos"]
125127
cockroachdb = []
128+
db2 = ["sqlalchemy", "ibm_db_sa"]
126129
elasticsearch = []
127130
generic = ["httpx"]
128131
test_module_import = ["httpx"]

0 commit comments

Comments
 (0)