Skip to content

Commit ad8a8be

Browse files
committed
Adding support for Cassandra and Scylla
This add the support for those cassandra based dbs and their drivers, cassandra-driver, scylla-driver Ref: https://cassandra.apache.org/ Ref: https://www.scylladb.com/ Ref: https://pypi.org/project/cassandra-driver/ Ref: https://pypi.org/project/scylla-driver/
1 parent 29629e2 commit ad8a8be

File tree

11 files changed

+190
-10
lines changed

11 files changed

+190
-10
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ jobs:
6767
- keycloak.py
6868
- arangodb.py
6969
- azurite.py
70+
- scylladb.py
71+
- cassandra.py
7072
runs-on: ubuntu-latest
7173
steps:
7274
- uses: actions/checkout@v2

docs/database.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ Allows to spin up database images such as MySQL, PostgreSQL, MariaDB, Oracle XE,
1212
.. autoclass:: testcontainers.clickhouse.ClickHouseContainer
1313
.. autoclass:: testcontainers.neo4j.Neo4jContainer
1414
.. autoclass:: testcontainers.arangodb.ArangoDbContainer
15+
.. autoclass:: testcontainers.cassandra.CassandraContainer
16+
.. autoclass:: testcontainers.scylla.ScyllaContainer

requirements.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-e file:.[docker-compose,mysql,oracle,postgresql,selenium,google-cloud-pubsub,minio,mongo,redis,mssqlserver,neo4j,kafka,rabbitmq,clickhouse,keycloak,arangodb,azurite]
1+
-e file:.[docker-compose,mysql,oracle,postgresql,selenium,google-cloud-pubsub,minio,mongo,redis,mssqlserver,neo4j,kafka,rabbitmq,clickhouse,keycloak,arangodb,azurite,cassandra,scylla]
22
codecov>=2.1.0
33
cryptography<37
44
flake8<3.8.0 # 3.8.0 adds a dependency on importlib-metadata which conflicts with other packages.

requirements/3.10.txt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ bleach==5.0.1
3636
# via readme-renderer
3737
cachetools==5.2.0
3838
# via google-auth
39+
cassandra-driver==3.25.0
40+
# via testcontainers
3941
certifi==2022.12.7
4042
# via
4143
# minio
@@ -48,6 +50,8 @@ cffi==1.15.1
4850
# pynacl
4951
charset-normalizer==2.1.1
5052
# via requests
53+
click==8.1.3
54+
# via geomet
5155
clickhouse-driver==0.2.5
5256
# via testcontainers
5357
codecov==2.1.12
@@ -96,6 +100,10 @@ exceptiongroup==1.0.4
96100
# trio
97101
flake8==3.7.9
98102
# via -r requirements.in
103+
geomet==0.2.1.post1
104+
# via
105+
# cassandra-driver
106+
# scylla-driver
99107
google-api-core[grpc]==2.11.0
100108
# via google-cloud-pubsub
101109
google-auth==2.15.0
@@ -227,7 +235,7 @@ pytest==7.2.0
227235
# pytest-cov
228236
pytest-cov==4.0.0
229237
# via -r requirements.in
230-
python-arango==7.5.3
238+
python-arango==7.5.4
231239
# via testcontainers
232240
python-dateutil==2.8.2
233241
# via pg8000
@@ -245,7 +253,9 @@ pytz==2022.6
245253
pytz-deprecation-shim==0.1.0.post0
246254
# via tzlocal
247255
pyyaml==5.4.1
248-
# via docker-compose
256+
# via
257+
# docker-compose
258+
# scylla-driver
249259
readme-renderer==37.3
250260
# via twine
251261
redis==4.4.0
@@ -281,6 +291,8 @@ rsa==4.9
281291
# python-jose
282292
scramp==1.4.4
283293
# via pg8000
294+
scylla-driver==3.25.10
295+
# via testcontainers
284296
secretstorage==3.3.3
285297
# via keyring
286298
selenium==4.7.2
@@ -289,13 +301,16 @@ six==1.16.0
289301
# via
290302
# azure-core
291303
# bleach
304+
# cassandra-driver
292305
# dockerpty
293306
# ecdsa
307+
# geomet
294308
# google-auth
295309
# isodate
296310
# jsonschema
297311
# paramiko
298312
# python-dateutil
313+
# scylla-driver
299314
# websocket-client
300315
sniffio==1.3.0
301316
# via trio
@@ -317,7 +332,7 @@ sphinxcontrib-qthelp==1.0.3
317332
# via sphinx
318333
sphinxcontrib-serializinghtml==1.1.5
319334
# via sphinx
320-
sqlalchemy==1.4.44
335+
sqlalchemy==1.4.45
321336
# via testcontainers
322337
texttable==1.6.7
323338
# via docker-compose

requirements/3.8.txt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ bleach==5.0.1
4040
# via readme-renderer
4141
cachetools==5.2.0
4242
# via google-auth
43+
cassandra-driver==3.25.0
44+
# via testcontainers
4345
certifi==2022.12.7
4446
# via
4547
# minio
@@ -52,6 +54,8 @@ cffi==1.15.1
5254
# pynacl
5355
charset-normalizer==2.1.1
5456
# via requests
57+
click==8.1.3
58+
# via geomet
5559
clickhouse-driver==0.2.5
5660
# via testcontainers
5761
codecov==2.1.12
@@ -100,6 +104,10 @@ exceptiongroup==1.0.4
100104
# trio
101105
flake8==3.7.9
102106
# via -r requirements.in
107+
geomet==0.2.1.post1
108+
# via
109+
# cassandra-driver
110+
# scylla-driver
103111
google-api-core[grpc]==2.11.0
104112
# via google-cloud-pubsub
105113
google-auth==2.15.0
@@ -232,7 +240,7 @@ pytest==7.2.0
232240
# pytest-cov
233241
pytest-cov==4.0.0
234242
# via -r requirements.in
235-
python-arango==7.5.3
243+
python-arango==7.5.4
236244
# via testcontainers
237245
python-dateutil==2.8.2
238246
# via pg8000
@@ -250,7 +258,9 @@ pytz==2022.6
250258
pytz-deprecation-shim==0.1.0.post0
251259
# via tzlocal
252260
pyyaml==5.4.1
253-
# via docker-compose
261+
# via
262+
# docker-compose
263+
# scylla-driver
254264
readme-renderer==37.3
255265
# via twine
256266
redis==4.4.0
@@ -286,6 +296,8 @@ rsa==4.9
286296
# python-jose
287297
scramp==1.4.4
288298
# via pg8000
299+
scylla-driver==3.25.10
300+
# via testcontainers
289301
secretstorage==3.3.3
290302
# via keyring
291303
selenium==4.7.2
@@ -294,13 +306,16 @@ six==1.16.0
294306
# via
295307
# azure-core
296308
# bleach
309+
# cassandra-driver
297310
# dockerpty
298311
# ecdsa
312+
# geomet
299313
# google-auth
300314
# isodate
301315
# jsonschema
302316
# paramiko
303317
# python-dateutil
318+
# scylla-driver
304319
# websocket-client
305320
sniffio==1.3.0
306321
# via trio
@@ -322,7 +337,7 @@ sphinxcontrib-qthelp==1.0.3
322337
# via sphinx
323338
sphinxcontrib-serializinghtml==1.1.5
324339
# via sphinx
325-
sqlalchemy==1.4.44
340+
sqlalchemy==1.4.45
326341
# via testcontainers
327342
texttable==1.6.7
328343
# via docker-compose

requirements/3.9.txt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ bleach==5.0.1
3636
# via readme-renderer
3737
cachetools==5.2.0
3838
# via google-auth
39+
cassandra-driver==3.25.0
40+
# via testcontainers
3941
certifi==2022.12.7
4042
# via
4143
# minio
@@ -48,6 +50,8 @@ cffi==1.15.1
4850
# pynacl
4951
charset-normalizer==2.1.1
5052
# via requests
53+
click==8.1.3
54+
# via geomet
5155
clickhouse-driver==0.2.5
5256
# via testcontainers
5357
codecov==2.1.12
@@ -96,6 +100,10 @@ exceptiongroup==1.0.4
96100
# trio
97101
flake8==3.7.9
98102
# via -r requirements.in
103+
geomet==0.2.1.post1
104+
# via
105+
# cassandra-driver
106+
# scylla-driver
99107
google-api-core[grpc]==2.11.0
100108
# via google-cloud-pubsub
101109
google-auth==2.15.0
@@ -228,7 +236,7 @@ pytest==7.2.0
228236
# pytest-cov
229237
pytest-cov==4.0.0
230238
# via -r requirements.in
231-
python-arango==7.5.3
239+
python-arango==7.5.4
232240
# via testcontainers
233241
python-dateutil==2.8.2
234242
# via pg8000
@@ -246,7 +254,9 @@ pytz==2022.6
246254
pytz-deprecation-shim==0.1.0.post0
247255
# via tzlocal
248256
pyyaml==5.4.1
249-
# via docker-compose
257+
# via
258+
# docker-compose
259+
# scylla-driver
250260
readme-renderer==37.3
251261
# via twine
252262
redis==4.4.0
@@ -282,6 +292,8 @@ rsa==4.9
282292
# python-jose
283293
scramp==1.4.4
284294
# via pg8000
295+
scylla-driver==3.25.10
296+
# via testcontainers
285297
secretstorage==3.3.3
286298
# via keyring
287299
selenium==4.7.2
@@ -290,13 +302,16 @@ six==1.16.0
290302
# via
291303
# azure-core
292304
# bleach
305+
# cassandra-driver
293306
# dockerpty
294307
# ecdsa
308+
# geomet
295309
# google-auth
296310
# isodate
297311
# jsonschema
298312
# paramiko
299313
# python-dateutil
314+
# scylla-driver
300315
# websocket-client
301316
sniffio==1.3.0
302317
# via trio
@@ -318,7 +333,7 @@ sphinxcontrib-qthelp==1.0.3
318333
# via sphinx
319334
sphinxcontrib-serializinghtml==1.1.5
320335
# via sphinx
321-
sqlalchemy==1.4.44
336+
sqlalchemy==1.4.45
322337
# via testcontainers
323338
texttable==1.6.7
324339
# via docker-compose

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
'keycloak': ['python-keycloak'],
7373
'arangodb': ['python-arango'],
7474
'azurite': ['azure-storage-blob'],
75+
'cassandra': ['cassandra-driver'],
76+
'scylla': ['scylla-driver'],
7577
},
7678
long_description_content_type="text/x-rst",
7779
long_description=long_description,

testcontainers/cassandra.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from testcontainers.core.config import MAX_TRIES
2+
from testcontainers.core.generic import DockerContainer
3+
from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
4+
5+
6+
class CassandraContainer(DockerContainer):
7+
"""
8+
Cassandra database container.
9+
10+
Example
11+
-------
12+
::
13+
14+
with CassandraContainer() as cassandra:
15+
cluster = cassandra.get_cluster()
16+
with cluster.connect() as session:
17+
session.execute(
18+
"CREATE KEYSPACE keyspace1 WITH replication = "
19+
"{'class': 'SimpleStrategy', 'replication_factor': '1'};")
20+
21+
"""
22+
def __init__(self, image="rinscy/cassandra:latest", ports_to_expose=[9042]):
23+
super(CassandraContainer, self).__init__(image)
24+
self.ports_to_expose = ports_to_expose
25+
self.with_exposed_ports(*self.ports_to_expose)
26+
27+
@wait_container_is_ready()
28+
def _connect(self):
29+
wait_for_logs(
30+
self,
31+
predicate="Starting listening for CQL clients",
32+
timeout=MAX_TRIES)
33+
cluster = self.get_cluster()
34+
cluster.connect()
35+
36+
def start(self):
37+
super(CassandraContainer, self).start()
38+
self._connect()
39+
return self
40+
41+
def get_cluster(self, **kwargs):
42+
from cassandra.cluster import Cluster
43+
container = self.get_wrapped_container()
44+
container.reload()
45+
hostname = container.attrs['NetworkSettings']['IPAddress']
46+
return Cluster(contact_points=[hostname], **kwargs)

testcontainers/scylla.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from testcontainers.core.config import MAX_TRIES
2+
from testcontainers.core.generic import DockerContainer
3+
from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
4+
5+
6+
class ScyllaContainer(DockerContainer):
7+
"""
8+
Scylla database container.
9+
10+
Example
11+
-------
12+
::
13+
14+
with ScyllaContainer() as scylla:
15+
cluster = scylla.get_cluster()
16+
with cluster.connect() as session:
17+
session.execute(
18+
"CREATE KEYSPACE keyspace1 WITH replication "
19+
"= {'class': 'SimpleStrategy', 'replication_factor': '1'};")
20+
21+
"""
22+
def __init__(self, image="scylladb/scylla:latest", ports_to_expose=[9042]):
23+
super(ScyllaContainer, self).__init__(image)
24+
self.ports_to_expose = ports_to_expose
25+
self.with_exposed_ports(*self.ports_to_expose)
26+
self.with_command("--skip-wait-for-gossip-to-settle=0")
27+
28+
@wait_container_is_ready()
29+
def _connect(self):
30+
wait_for_logs(
31+
self,
32+
predicate="Starting listening for CQL clients",
33+
timeout=MAX_TRIES)
34+
cluster = self.get_cluster()
35+
cluster.connect()
36+
37+
def start(self):
38+
super(ScyllaContainer, self).start()
39+
self._connect()
40+
return self
41+
42+
def get_cluster(self, **kwargs):
43+
from cassandra.cluster import Cluster
44+
container = self.get_wrapped_container()
45+
container.reload()
46+
hostname = container.attrs['NetworkSettings']['IPAddress']
47+
return Cluster(contact_points=[hostname], **kwargs)

tests/test_cassandra.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from testcontainers.cassandra import CassandraContainer
2+
3+
4+
def test_docker_run_cassandra():
5+
with CassandraContainer() as cassandra:
6+
cluster = cassandra.get_cluster()
7+
with cluster.connect() as session:
8+
session.execute(
9+
"CREATE KEYSPACE keyspace1 WITH replication = "
10+
"{'class': 'SimpleStrategy', 'replication_factor': '1'};")
11+
session.execute(
12+
"CREATE TABLE keyspace1.table1 (key1 int, key2 int, PRIMARY KEY (key1));")
13+
session.execute("INSERT INTO keyspace1.table1 (key1,key2) values (1,2);")
14+
15+
response = session.execute("SELECT * FROM keyspace1.table1")
16+
17+
assert response.one().key1 == 1
18+
assert response.one().key2 == 2

0 commit comments

Comments
 (0)