Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove class-level variables from containers. #296

Merged
merged 34 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5f5536a
Remove class-level variables from azurite.
tillahoffmann Jan 7, 2023
7a06617
Precompute `TIMEOUT`.
tillahoffmann Jan 7, 2023
ad3aef7
Remove static variables from `clickhouse`.
tillahoffmann Jan 7, 2023
355ac71
Remove static variables from `kafka`.
tillahoffmann Jan 7, 2023
d357376
Remove static variables from `keycloak`.
tillahoffmann Jan 7, 2023
bfb8200
Remove static variables from `mongodb`.
tillahoffmann Jan 7, 2023
e072dba
Remove static variables from `mssql`.
tillahoffmann Jan 7, 2023
1d6e402
Remove static variables from `mysql`.
tillahoffmann Jan 7, 2023
e8e4078
Remove static variables from `neo4j`.
tillahoffmann Jan 7, 2023
043dea0
Rename user to username in `opensearch`.
tillahoffmann Jan 7, 2023
d407859
Remove static variables from `postgres`.
tillahoffmann Jan 7, 2023
23e93b2
Remove static variables from `rabbitmq`.
tillahoffmann Jan 7, 2023
33014e4
Use f-strings throughout.
tillahoffmann Jan 7, 2023
95dc6ca
Fix typo in `ClickHouseContainer.__init__`.
tillahoffmann Jan 7, 2023
14761b1
Fix default username in `neo4j`.
tillahoffmann Jan 7, 2023
cf6c585
Rename `port_to_expose` to `port` and use f-strings in tests.
tillahoffmann Jan 7, 2023
3c28cdd
Remove erroneous `with_command` statement.
tillahoffmann Jan 7, 2023
89822f7
Rename `db_user` to `username` in tests.
tillahoffmann Jan 7, 2023
deebd05
Remove unused function in docker api test.
tillahoffmann Jan 7, 2023
44d9b3d
Rename `db_name` to `dbname`.
tillahoffmann Jan 7, 2023
7f08e44
Remove uninformative module docstrings.
tillahoffmann Jan 7, 2023
f5a16cb
Rename `port_to_expose` to `port` for Redis and Elasticsearch.
tillahoffmann Jan 7, 2023
3add8cb
Use utility function to raise for deprecated parameters.
tillahoffmann Jan 7, 2023
f881c80
Remove `ports_to_expose` from Azurite container.
tillahoffmann Feb 18, 2023
0a1b372
Rename `port_to_expose` to `port`.
tillahoffmann Feb 18, 2023
6dc9b8d
Expose port configuration for selenium.
tillahoffmann Feb 18, 2023
76a9bfa
Remove `db_` from arango tests so we can search for `db_name`.
tillahoffmann Feb 18, 2023
e2cc996
Fix incorrect port unpacking.
tillahoffmann Feb 18, 2023
76a23d0
Merge branch 'master' into pythonic
tillahoffmann Apr 11, 2023
c281a8e
Change error messages to title case.
tillahoffmann Apr 11, 2023
6c653f7
Xfail docker-in-docker test.
tillahoffmann Apr 11, 2023
1be54d4
Update requirements.
tillahoffmann Apr 11, 2023
703d975
Add missing colon to error message.
tillahoffmann Apr 11, 2023
cfb2802
Allow positional port argument for `Neo4jContainer`.
tillahoffmann Apr 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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