Skip to content

Commit

Permalink
Merge pull request #296 from tillahoffmann/pythonic
Browse files Browse the repository at this point in the history
Remove class-level variables from containers.
  • Loading branch information
tillahoffmann committed Apr 11, 2023
2 parents 6371c83 + cfb2802 commit 63ad996
Show file tree
Hide file tree
Showing 39 changed files with 496 additions and 549 deletions.
16 changes: 9 additions & 7 deletions arangodb/testcontainers/arangodb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
ArangoDB container support.
"""
from os import environ
from testcontainers.core.config import MAX_TRIES
from testcontainers.core.config import TIMEOUT
from testcontainers.core.generic import DbContainer
from testcontainers.core.utils import raise_for_deprecated_parameter
from testcontainers.core.waiting_utils import wait_for_logs
import typing

Expand Down Expand Up @@ -36,15 +37,15 @@ class ArangoDbContainer(DbContainer):

def __init__(self,
image: str = "arangodb:latest",
port_to_expose: int = 8529,
port: int = 8529,
arango_root_password: str = "passwd",
arango_no_auth: typing.Optional[bool] = None,
arango_random_root_password: typing.Optional[bool] = None,
**kwargs) -> None:
"""
Args:
image: Actual docker image/tag to pull.
port_to_expose: Port the container needs to expose.
port: Port the container needs to expose.
arango_root_password: Start ArangoDB with the given password for root. Defaults to the
environment variable `ARANGO_ROOT_PASSWORD` if `None`.
arango_no_auth: Disable authentication completely. Defaults to the environment variable
Expand All @@ -53,9 +54,10 @@ def __init__(self,
the environment variable `ARANGO_NO_AUTH` if `None` or `False` if the environment
variable is not available.
"""
raise_for_deprecated_parameter(kwargs, "port_to_expose", "port")
super().__init__(image=image, **kwargs)
self.port_to_expose = port_to_expose
self.with_exposed_ports(self.port_to_expose)
self.port = port
self.with_exposed_ports(self.port)

# See https://www.arangodb.com/docs/stable/deployment-single-instance-manual-start.html for
# details. We convert to int then to bool because Arango uses the string literal "1" to
Expand All @@ -77,8 +79,8 @@ def _configure(self) -> None:
self.with_env("ARANGO_RANDOM_ROOT_PASSWORD", "1")

def get_connection_url(self) -> str:
port = self.get_exposed_port(self.port_to_expose)
port = self.get_exposed_port(self.port)
return f"http://{self.get_container_host_ip()}:{port}"

def _connect(self) -> None:
wait_for_logs(self, predicate="is ready for business", timeout=MAX_TRIES)
wait_for_logs(self, predicate="is ready for business", timeout=TIMEOUT)
18 changes: 9 additions & 9 deletions arangodb/tests/test_arangodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@
ARANGODB_IMAGE_NAME = 'arangodb'


def arango_test_ops(arango_client, expeced_version, db_user='root', db_pass=''):
def arango_test_ops(arango_client, expeced_version, username='root', password=''):
"""
Basic ArangoDB operations to test DB really up and running.
"""
students_to_insert_cnt = 3

# Taken from https://github.com/ArangoDB-Community/python-arango/blob/main/README.md
# Connect to "_system" database as root user.
sys_db = arango_client.db("_system", username=db_user, password=db_pass)
sys_db = arango_client.db("_system", username=username, password=password)
assert sys_db.version() == expeced_version

# Create a new database named "test".
sys_db.create_database("test")

# Connect to "test" database as root user.
database = arango_client.db("test", username=db_user, password=db_pass)
database = arango_client.db("test", username=username, password=password)

# Create a new collection named "students".
students = database.create_collection("students")
Expand All @@ -50,7 +50,7 @@ def test_docker_run_arango():
"""
image_version = '3.9.1'
image = f'{ARANGODB_IMAGE_NAME}:{image_version}'
arango_db_root_password = 'passwd'
arango_root_password = 'passwd'

with ArangoDbContainer(image) as arango:
client = ArangoClient(hosts=arango.get_connection_url())
Expand All @@ -63,7 +63,7 @@ def test_docker_run_arango():
arango_test_ops(
arango_client=client,
expeced_version=image_version,
db_pass=arango_db_root_password)
password=arango_root_password)


def test_docker_run_arango_without_auth():
Expand All @@ -79,7 +79,7 @@ def test_docker_run_arango_without_auth():
arango_test_ops(
arango_client=client,
expeced_version=image_version,
db_pass='')
password='')


def test_docker_run_arango_older_version():
Expand All @@ -100,7 +100,7 @@ def test_docker_run_arango_older_version():
arango_test_ops(
arango_client=client,
expeced_version=image_version,
db_pass='')
password='')


def test_docker_run_arango_random_root_password():
Expand All @@ -109,12 +109,12 @@ def test_docker_run_arango_random_root_password():
"""
image_version = '3.9.1'
image = f'{ARANGODB_IMAGE_NAME}:{image_version}'
arango_db_root_password = 'passwd'
arango_root_password = 'passwd'

with ArangoDbContainer(image, arango_random_root_password=True) as arango:
client = ArangoClient(hosts=arango.get_connection_url())

# Test invalid auth (we don't know the password in random mode)
with pytest.raises(ServerVersionError):
sys_db = client.db("_system", username='root', password=arango_db_root_password)
sys_db = client.db("_system", username='root', password=arango_root_password)
assert sys_db.version() == image_version
96 changes: 44 additions & 52 deletions azurite/testcontainers/azurite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,88 +12,80 @@
# under the License.
import os
import socket
from typing import Iterable, Optional
from typing import Optional

from testcontainers.core.container import DockerContainer
from testcontainers.core.utils import raise_for_deprecated_parameter
from testcontainers.core.waiting_utils import wait_container_is_ready


class AzuriteContainer(DockerContainer):
"""
The example below spins up an Azurite container and
shows an example to create a Blob service client with the container. The method
:code:`get_connection_string` can be used to create a client for Blob service, Queue service
and Table service.
The example below spins up an Azurite container and
shows an example to create a Blob service client with the container. The method
:code:`get_connection_string` can be used to create a client for Blob service, Queue service
and Table service.
Example:
Example:
.. doctest::
.. doctest::
>>> from testcontainers.azurite import AzuriteContainer
>>> from azure.storage.blob import BlobServiceClient
>>> from testcontainers.azurite import AzuriteContainer
>>> from azure.storage.blob import BlobServiceClient
>>> with AzuriteContainer() as azurite_container:
... connection_string = azurite_container.get_connection_string()
... client = BlobServiceClient.from_connection_string(
... connection_string,
... api_version="2019-12-12"
... )
"""

_AZURITE_ACCOUNT_NAME = os.environ.get("AZURITE_ACCOUNT_NAME", "devstoreaccount1")
_AZURITE_ACCOUNT_KEY = os.environ.get("AZURITE_ACCOUNT_KEY", "Eby8vdM02xNOcqFlqUwJPLlmEtlCDX"
"J1OUzFT50uSRZ6IFsuFq2UVErCz4I6"
"tq/K1SZFPTOtr/KBHBeksoGMGw==")

_BLOB_SERVICE_PORT = 10_000
_QUEUE_SERVICE_PORT = 10_001
_TABLE_SERVICE_PORT = 10_002

def __init__(self, image: str = "mcr.microsoft.com/azure-storage/azurite:latest",
ports_to_expose: Optional[Iterable[int]] = None, **kwargs) -> None:
>>> with AzuriteContainer() as azurite_container:
... connection_string = azurite_container.get_connection_string()
... client = BlobServiceClient.from_connection_string(
... connection_string,
... api_version="2019-12-12"
... )
"""
def __init__(self, image: str = "mcr.microsoft.com/azure-storage/azurite:latest", *,
blob_service_port: int = 10_000, queue_service_port: int = 10_001,
table_service_port: int = 10_002, account_name: Optional[str] = None,
account_key: Optional[str] = None, **kwargs) \
-> None:
""" Constructs an AzuriteContainer.
Args:
image: Expects an image with tag.
ports_to_expose: List with port numbers to expose.
**kwargs: Keyword arguments passed to super class.
"""
super().__init__(image=image, **kwargs)
self.account_name = account_name or os.environ.get(
"AZURITE_ACCOUNT_NAME", "devstoreaccount1")
self.account_key = account_key or os.environ.get(
"AZURITE_ACCOUNT_KEY", "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/"
"K1SZFPTOtr/KBHBeksoGMGw==")

if ports_to_expose is None:
ports_to_expose = [
self._BLOB_SERVICE_PORT,
self._QUEUE_SERVICE_PORT,
self._TABLE_SERVICE_PORT
]

if len(ports_to_expose) == 0:
raise ValueError("Expected a list with port numbers to expose")
raise_for_deprecated_parameter(kwargs, "ports_to_expose", "container.with_exposed_ports")
self.blob_service_port = blob_service_port
self.queue_service_port = queue_service_port
self.table_service_port = table_service_port

self.with_exposed_ports(*ports_to_expose)
self.with_env("AZURITE_ACCOUNTS",
f"{self._AZURITE_ACCOUNT_NAME}:{self._AZURITE_ACCOUNT_KEY}")
self.with_exposed_ports(blob_service_port, queue_service_port, table_service_port)
self.with_env("AZURITE_ACCOUNTS", f"{self.account_name}:{self.account_key}")

def get_connection_string(self) -> str:
host_ip = self.get_container_host_ip()
connection_string = f"DefaultEndpointsProtocol=http;" \
f"AccountName={self._AZURITE_ACCOUNT_NAME};" \
f"AccountKey={self._AZURITE_ACCOUNT_KEY};"
f"AccountName={self.account_name};" \
f"AccountKey={self.account_key};"

if self._BLOB_SERVICE_PORT in self.ports:
if self.blob_service_port in self.ports:
connection_string += f"BlobEndpoint=http://{host_ip}:" \
f"{self.get_exposed_port(self._BLOB_SERVICE_PORT)}" \
f"/{self._AZURITE_ACCOUNT_NAME};"
f"{self.get_exposed_port(self.blob_service_port)}" \
f"/{self.account_name};"

if self._QUEUE_SERVICE_PORT in self.ports:
if self.queue_service_port in self.ports:
connection_string += f"QueueEndpoint=http://{host_ip}:" \
f"{self.get_exposed_port(self._QUEUE_SERVICE_PORT)}" \
f"/{self._AZURITE_ACCOUNT_NAME};"
f"{self.get_exposed_port(self.queue_service_port)}" \
f"/{self.account_name};"

if self._TABLE_SERVICE_PORT in self.ports:
if self.table_service_port in self.ports:
connection_string += f"TableEndpoint=http://{host_ip}:" \
f"{self.get_exposed_port(self._TABLE_SERVICE_PORT)}" \
f"/{self._AZURITE_ACCOUNT_NAME};"
f"{self.get_exposed_port(self.table_service_port)}" \
f"/{self.account_name};"

return connection_string

Expand Down
45 changes: 18 additions & 27 deletions clickhouse/testcontainers/clickhouse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from clickhouse_driver.errors import Error

from testcontainers.core.generic import DbContainer
from testcontainers.core.utils import raise_for_deprecated_parameter
from testcontainers.core.waiting_utils import wait_container_is_ready


Expand All @@ -39,43 +40,33 @@ class ClickHouseContainer(DbContainer):
... client.execute("select 'working'")
[('working',)]
"""

CLICKHOUSE_USER = os.environ.get("CLICKHOUSE_USER", "test")
CLICKHOUSE_PASSWORD = os.environ.get("CLICKHOUSE_PASSWORD", "test")
CLICKHOUSE_DB = os.environ.get("CLICKHOUSE_DB", "test")

def __init__(
self,
image: str = "clickhouse/clickhouse-server:latest",
port: int = 9000,
user: Optional[str] = None,
password: Optional[str] = None,
dbname: Optional[str] = None
) -> None:
super().__init__(image=image)

self.CLICKHOUSE_USER = user or self.CLICKHOUSE_USER
self.CLICKHOUSE_PASSWORD = password or self.CLICKHOUSE_PASSWORD
self.CLICKHOUSE_DB = dbname or self.CLICKHOUSE_DB
self.port_to_expose = port
self.with_exposed_ports(self.port_to_expose)
def __init__(self, image: str = "clickhouse/clickhouse-server:latest", port: int = 9000,
username: Optional[str] = None, password: Optional[str] = None,
dbname: Optional[str] = None, **kwargs) -> None:
raise_for_deprecated_parameter(kwargs, "user", "username")
super().__init__(image=image, **kwargs)
self.username = username or os.environ.get("CLICKHOUSE_USER", "test")
self.password = password or os.environ.get("CLICKHOUSE_PASSWORD", "test")
self.dbname = dbname or os.environ.get("CLICKHOUSE_DB", "test")
self.port = port
self.with_exposed_ports(self.port)

@wait_container_is_ready(Error, EOFError)
def _connect(self) -> None:
with clickhouse_driver.Client.from_url(self.get_connection_url()) as client:
client.execute("SELECT version()")

def _configure(self) -> None:
self.with_env("CLICKHOUSE_USER", self.CLICKHOUSE_USER)
self.with_env("CLICKHOUSE_PASSWORD", self.CLICKHOUSE_PASSWORD)
self.with_env("CLICKHOUSE_DB", self.CLICKHOUSE_DB)
self.with_env("CLICKHOUSE_USER", self.username)
self.with_env("CLICKHOUSE_PASSWORD", self.password)
self.with_env("CLICKHOUSE_DB", self.dbname)

def get_connection_url(self, host: Optional[str] = None) -> str:
return self._create_connection_url(
dialect="clickhouse",
username=self.CLICKHOUSE_USER,
password=self.CLICKHOUSE_PASSWORD,
db_name=self.CLICKHOUSE_DB,
username=self.username,
password=self.password,
dbname=self.dbname,
host=host,
port=self.port_to_expose,
port=self.port,
)
11 changes: 2 additions & 9 deletions compose/testcontainers/compose/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
"""
Docker Compose Support
======================
Allows to spin up services configured via :code:`docker-compose.yml`.
"""

import requests
import subprocess
from typing import Iterable, List, Optional, Tuple, Union
Expand Down Expand Up @@ -36,13 +29,13 @@ class DockerCompose:
host = compose.get_service_host("hub", 4444)
port = compose.get_service_port("hub", 4444)
driver = webdriver.Remote(
command_executor=("http://{}:{}/wd/hub".format(host,port)),
command_executor=(f"http://{host}:{port}/wd/hub"),
desired_capabilities=CHROME,
)
driver.get("http://automation-remarks.com")
stdout, stderr = compose.get_logs()
if stderr:
print("Errors\\n:{}".format(stderr))
print(f"Errors\\n:{stderr}")
.. code-block:: yaml
Expand Down
1 change: 1 addition & 0 deletions core/testcontainers/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

MAX_TRIES = int(environ.get("TC_MAX_TRIES", 120))
SLEEP_TIME = int(environ.get("TC_POOLING_INTERVAL", 1))
TIMEOUT = MAX_TRIES * SLEEP_TIME
4 changes: 2 additions & 2 deletions core/testcontainers/core/docker_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def port(self, container_id: str, port: int) -> int:
"""
port_mappings = self.client.api.port(container_id, port)
if not port_mappings:
raise ConnectionError(f'port mapping for container {container_id} and port {port} is '
raise ConnectionError(f'Port mapping for container {container_id} and port {port} is '
'not available')
return port_mappings[0]["HostPort"]

Expand All @@ -71,7 +71,7 @@ def get_container(self, container_id: str) -> Container:
"""
containers = self.client.api.containers(filters={'id': container_id})
if not containers:
raise RuntimeError(f'could not get container with id {container_id}')
raise RuntimeError(f'Could not get container with id {container_id}')
return containers[0]

def bridge_ip(self, container_id: str) -> str:
Expand Down
Loading

0 comments on commit 63ad996

Please sign in to comment.