Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion arangodb/testcontainers/arangodb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ArangoDbContainer(DbContainer):
>>> from testcontainers.arangodb import ArangoDbContainer
>>> from arango import ArangoClient

>>> with ArangoDbContainer("arangodb:3.9.1") as arango:
>>> with ArangoDbContainer("arangodb:3.11.1") as arango:
... client = ArangoClient(hosts=arango.get_connection_url())
...
... # Connect
Expand Down
6 changes: 3 additions & 3 deletions arangodb/tests/test_arangodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_docker_run_arango():
"""
Test ArangoDB container with default settings.
"""
image_version = '3.9.1'
image_version = '3.11.1'
image = f'{ARANGODB_IMAGE_NAME}:{image_version}'
arango_root_password = 'passwd'

Expand All @@ -70,7 +70,7 @@ def test_docker_run_arango_without_auth():
"""
Test ArangoDB container with ARANGO_NO_AUTH var set.
"""
image_version = '3.9.1'
image_version = '3.11.1'
image = f'{ARANGODB_IMAGE_NAME}:{image_version}'

with ArangoDbContainer(image, arango_no_auth=True) as arango:
Expand Down Expand Up @@ -107,7 +107,7 @@ def test_docker_run_arango_random_root_password():
"""
Test ArangoDB container with ARANGO_RANDOM_ROOT_PASSWORD var set.
"""
image_version = '3.9.1'
image_version = '3.11.1'
image = f'{ARANGODB_IMAGE_NAME}:{image_version}'
arango_root_password = 'passwd'

Expand Down
4 changes: 2 additions & 2 deletions elasticsearch/tests/test_elasticsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from testcontainers.elasticsearch import ElasticSearchContainer


# The versions below were the current supported versions at time of writing (2022-08-11)
@pytest.mark.parametrize('version', ['6.8.23', '7.17.5', '8.3.3'])
# The versions below should reflect the latest stable releases of maintained versions
@pytest.mark.parametrize('version', ['7.17.10', '8.8.1'])
def test_docker_run_elasticsearch(version):
with ElasticSearchContainer(f'elasticsearch:{version}') as es:
resp = urllib.request.urlopen(es.get_url())
Expand Down
4 changes: 2 additions & 2 deletions google/tests/test_google.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

def test_pubsub_container():
pubsub: PubSubContainer
with PubSubContainer() as pubsub:
wait_for_logs(pubsub, r"Server started, listening on \d+", timeout=60)
with PubSubContainer("google/cloud-sdk:316.0.0-emulators") as pubsub:
wait_for_logs(pubsub, r"Server started, listening on \d+", timeout=120)
# Create a new topic
publisher = pubsub.get_publisher_client()
topic_path = publisher.topic_path(pubsub.project, "my-topic")
Expand Down
37 changes: 12 additions & 25 deletions keycloak/testcontainers/keycloak/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
import requests

from keycloak import KeycloakAdmin

from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_container_is_ready
from testcontainers.core.waiting_utils import wait_for_logs
from typing import Optional


Expand All @@ -33,45 +32,33 @@ class KeycloakContainer(DockerContainer):
>>> with KeycloakContainer() as kc:
... keycloak = kc.get_client()
"""
def __init__(self, image="jboss/keycloak:latest", username: Optional[str] = None,
def __init__(self, image="quay.io/keycloak/keycloak:latest", username: Optional[str] = None,
password: Optional[str] = None, port: int = 8080) -> None:
super(KeycloakContainer, self).__init__(image=image)
self.username = username or os.environ.get("KEYCLOAK_USER", "test")
self.password = password or os.environ.get("KEYCLOAK_PASSWORD", "test")
self.username = username or os.environ.get("KEYCLOAK_ADMIN", "test")
self.password = password or os.environ.get("KEYCLOAK_ADMIN_PASSWORD", "test")
self.port = port
self.with_exposed_ports(self.port)

def _configure(self) -> None:
self.with_env("KEYCLOAK_USER", self.username)
self.with_env("KEYCLOAK_PASSWORD", self.password)
self.with_env("KEYCLOAK_ADMIN", self.username)
self.with_env("KEYCLOAK_ADMIN_PASSWORD", self.password)
self.with_command("start-dev")

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

@wait_container_is_ready(requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout)
def _connect(self) -> None:
url = self.get_url()
response = requests.get(f"{url}/auth", timeout=1)
response.raise_for_status()

def start(self) -> "KeycloakContainer":
self._configure()
super().start()
self._connect()
container = super().start()
wait_for_logs(container, 'Listening on:')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried this yet, but by passing KC_HEALTH_ENABLED=true as a environment variable I believe it might be possible to keep the self._connect() if instead of pooling {url}/auth we pool {url}/health.

return self

def get_client(self, **kwargs) -> KeycloakAdmin:
default_kwargs = dict(
server_url=f"{self.get_url()}/auth/",
return KeycloakAdmin(
server_url=f"{self.get_url()}/",
username=self.username,
password=self.password,
realm_name="master",
verify=True,
)
kwargs = {
**default_kwargs,
**kwargs
}
return KeycloakAdmin(**kwargs)
verify=True)
4 changes: 2 additions & 2 deletions keycloak/tests/test_keycloak.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

@pytest.mark.parametrize("version", ["16.1.1"])
def test_docker_run_keycloak(version: str):
with KeycloakContainer(f'jboss/keycloak:{version}') as kc:
kc.get_client().users_count()
with KeycloakContainer(f'quay.io/keycloak/keycloak:latest') as kc:
assert kc.get_client().users_count() == 1
4 changes: 3 additions & 1 deletion mssql/testcontainers/mssql/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class SqlServerContainer(DbContainer):
>>> import sqlalchemy
>>> from testcontainers.mssql import SqlServerContainer

>>> with SqlServerContainer() as mssql:
>>> # Use azure-sql-edge for Apple Silicon compatibility
>>> with SqlServerContainer('mcr.microsoft.com/azure-sql-edge') as mssql:
... engine = sqlalchemy.create_engine(mssql.get_connection_url())
... with engine.begin() as connection:
... result = connection.execute(sqlalchemy.text("select @@VERSION"))
Expand All @@ -37,6 +38,7 @@ def __init__(self, image: str = "mcr.microsoft.com/mssql/server:2019-latest",

def _configure(self) -> None:
self.with_env("SA_PASSWORD", self.password)
self.with_env("MSSQL_SA_PASSWORD", self.password)
self.with_env("SQLSERVER_USER", self.username)
self.with_env("SQLSERVER_DBNAME", self.dbname)
self.with_env("ACCEPT_EULA", 'Y')
Expand Down
6 changes: 6 additions & 0 deletions nginx/README.rst
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
.. autoclass:: testcontainers.nginx.NginxContainer

Creates a container with the `nginx <https://hub.docker.com/_/nginx/>`_ image
and waits for the container to accept connections.

Defaults to the latest release of nginx, and supports version 1.20+.
For older versions, use the generic DockerContainer class from `testcontainers-core`.
8 changes: 7 additions & 1 deletion nginx/testcontainers/nginx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@
# under the License.
from testcontainers.core.container import DockerContainer
from testcontainers.core.utils import raise_for_deprecated_parameter
from testcontainers.core.waiting_utils import wait_for_logs


class NginxContainer(DockerContainer):
def __init__(self, image: str = "nginx:latest", port: int = 80, **kwargs) -> None:
raise_for_deprecated_parameter(kwargs, "port_to_expose", "port")
super(NginxContainer, self).__init__(image, **kwargs)
super().__init__(image, **kwargs)
self.port = port
self.with_exposed_ports(self.port)

def start(self) -> DockerContainer:
container = super().start()
wait_for_logs(container, 'ready for start up')
return container
3 changes: 1 addition & 2 deletions nginx/tests/test_nginx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@


def test_docker_run_nginx():
nginx_container = NginxContainer("nginx:1.13.8")
with nginx_container as nginx:
with NginxContainer("nginx:1.24.0") as nginx:
url = f"http://{nginx.get_container_host_ip()}:{nginx.get_exposed_port(nginx.port)}/"
r = requests.get(url)
assert (r.status_code == 200)
Expand Down
8 changes: 8 additions & 0 deletions redis/README.rst
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
.. autoclass:: testcontainers.redis.RedisContainer



Creates a container with the `redis <https://hub.docker.com/_/redis>`_ image
and waits for the container to accept connections.

Defaults to the latest release of redis, and supports version 6.0+.
For older versions, use the generic DockerContainer class from `testcontainers-core`.
5 changes: 3 additions & 2 deletions redis/testcontainers/redis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import redis
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
from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
from typing import Optional


Expand Down Expand Up @@ -65,6 +65,7 @@ def get_client(self, **kwargs) -> redis.Redis:
)

def start(self) -> "RedisContainer":
super().start()
container = super().start()
wait_for_logs(container, 'Ready to accept connections')
self._connect()
return self
34 changes: 5 additions & 29 deletions redis/tests/test_redis.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
import time
import pytest

from testcontainers.redis import RedisContainer


def test_docker_run_redis():
config = RedisContainer()
with config as redis:
client = redis.get_client()
p = client.pubsub()
p.subscribe('test')
client.publish('test', 'new_msg')
msg = wait_for_message(p)
assert 'data' in msg
assert b'new_msg', msg['data']


def test_docker_run_redis_with_password():
config = RedisContainer(password="mypass")
with config as redis:
@pytest.mark.parametrize('version', ['7.0.11', '6.2.12'])
def test_redis_container(version: str):
image_name = f'redis:{version}'
with RedisContainer(image_name, password="mypass") as redis:
client = redis.get_client(decode_responses=True)
client.set("hello", "world")
assert client.get("hello") == "world"


def wait_for_message(pubsub, timeout=1, ignore_subscribe_messages=True):
now = time.time()
timeout = now + timeout
while now < timeout:
message = pubsub.get_message(
ignore_subscribe_messages=ignore_subscribe_messages)
if message is not None:
return message
time.sleep(0.01)
now = time.time()
return None