From 61e25721977b8c5d16a8ad1dbb4912aed53a34ae Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Sat, 3 Dec 2022 18:01:55 +0100 Subject: [PATCH 01/13] add minio container --- .github/workflows/main.yml | 1 + requirements.in | 2 +- requirements/3.10.txt | 33 +++++++++-------- requirements/3.7.txt | 33 +++++++++-------- requirements/3.8.txt | 33 +++++++++-------- requirements/3.9.txt | 33 +++++++++-------- setup.py | 3 ++ testcontainers/minio.py | 75 ++++++++++++++++++++++++++++++++++++++ tests/test_minio.py | 37 +++++++++++++++++++ 9 files changed, 189 insertions(+), 61 deletions(-) create mode 100644 testcontainers/minio.py create mode 100644 tests/test_minio.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 147e24d5f..02aab82a6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,6 +22,7 @@ jobs: - google.py - kafka.py - localstack.py + - minio.py - mongodb.py - neo4j.py - nginx.py diff --git a/requirements.in b/requirements.in index cd3bc8b87..f41b1b145 100644 --- a/requirements.in +++ b/requirements.in @@ -1,4 +1,4 @@ --e file:.[docker-compose,mysql,oracle,postgresql,selenium,google-cloud-pubsub,mongo,redis,mssqlserver,neo4j,kafka,rabbitmq,clickhouse,keycloak,arangodb,azurite] +-e file:.[docker-compose,mysql,oracle,postgresql,selenium,google-cloud-pubsub,minio,mongo,redis,mssqlserver,neo4j,kafka,rabbitmq,clickhouse,keycloak,arangodb,azurite] codecov>=2.1.0 cryptography<37 flake8<3.8.0 # 3.8.0 adds a dependency on importlib-metadata which conflicts with other packages. diff --git a/requirements/3.10.txt b/requirements/3.10.txt index a35dc9ee3..bd62b9486 100644 --- a/requirements/3.10.txt +++ b/requirements/3.10.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile --output-file=requirements/3.10.txt --resolver=backtracking requirements.in # @@ -38,6 +38,7 @@ cachetools==5.2.0 # via google-auth certifi==2022.9.24 # via + # minio # msrest # requests # selenium @@ -47,7 +48,7 @@ cffi==1.15.1 # pynacl charset-normalizer==2.1.1 # via requests -clickhouse-driver==0.2.4 +clickhouse-driver==0.2.5 # via testcontainers codecov==2.1.12 # via -r requirements.in @@ -95,9 +96,9 @@ exceptiongroup==1.0.4 # trio flake8==3.7.9 # via -r requirements.in -google-api-core[grpc]==2.10.2 +google-api-core[grpc]==2.11.0 # via google-cloud-pubsub -google-auth==2.14.1 +google-auth==2.15.0 # via google-api-core google-cloud-pubsub==1.7.2 # via testcontainers @@ -110,7 +111,7 @@ greenlet==2.0.1 # via sqlalchemy grpc-google-iam-v1==0.12.4 # via google-cloud-pubsub -grpcio==1.50.0 +grpcio==1.51.1 # via # google-api-core # googleapis-common-protos @@ -126,7 +127,7 @@ idna==3.4 # trio imagesize==1.4.1 # via sphinx -importlib-metadata==5.0.0 +importlib-metadata==5.1.0 # via # keyring # twine @@ -152,11 +153,13 @@ markupsafe==2.1.1 # via jinja2 mccabe==0.6.1 # via flake8 +minio==7.1.12 + # via testcontainers more-itertools==9.0.0 # via jaraco-classes msrest==0.7.1 # via azure-storage-blob -neo4j==5.2.1 +neo4j==5.3.0 # via testcontainers oauthlib==3.2.2 # via requests-oauthlib @@ -175,7 +178,7 @@ pg8000==1.29.3 # via -r requirements.in pika==1.3.1 # via testcontainers -pkginfo==1.8.3 +pkginfo==1.9.2 # via twine pluggy==1.0.0 # via pytest @@ -227,7 +230,7 @@ pytest==7.2.0 # pytest-cov pytest-cov==4.0.0 # via -r requirements.in -python-arango==7.5.2 +python-arango==7.5.3 # via testcontainers python-dateutil==2.8.2 # via pg8000 @@ -283,7 +286,7 @@ scramp==1.4.4 # via pg8000 secretstorage==3.3.3 # via keyring -selenium==4.6.0 +selenium==4.7.2 # via testcontainers six==1.16.0 # via @@ -292,7 +295,6 @@ six==1.16.0 # dockerpty # ecdsa # google-auth - # grpcio # isodate # jsonschema # paramiko @@ -332,17 +334,18 @@ trio==0.22.0 # trio-websocket trio-websocket==0.9.2 # via selenium -twine==4.0.1 +twine==4.0.2 # via -r requirements.in typing-extensions==4.4.0 # via azure-core -tzdata==2022.6 +tzdata==2022.7 # via pytz-deprecation-shim tzlocal==4.2 # via clickhouse-driver urllib3[socks]==1.26.13 # via # docker + # minio # python-arango # python-keycloak # requests @@ -358,7 +361,7 @@ wrapt==1.14.1 # via testcontainers wsproto==1.2.0 # via trio-websocket -zipp==3.10.0 +zipp==3.11.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/3.7.txt b/requirements/3.7.txt index 6252d8354..3db50f23f 100644 --- a/requirements/3.7.txt +++ b/requirements/3.7.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.7 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.7 +# by the following command: # # pip-compile --output-file=requirements/3.7.txt --resolver=backtracking requirements.in # @@ -44,6 +44,7 @@ cachetools==5.2.0 # via google-auth certifi==2022.9.24 # via + # minio # msrest # requests # selenium @@ -53,7 +54,7 @@ cffi==1.15.1 # pynacl charset-normalizer==2.1.1 # via requests -clickhouse-driver==0.2.4 +clickhouse-driver==0.2.5 # via testcontainers codecov==2.1.12 # via -r requirements.in @@ -101,9 +102,9 @@ exceptiongroup==1.0.4 # trio flake8==3.7.9 # via -r requirements.in -google-api-core[grpc]==2.10.2 +google-api-core[grpc]==2.11.0 # via google-cloud-pubsub -google-auth==2.14.1 +google-auth==2.15.0 # via google-api-core google-cloud-pubsub==1.7.2 # via testcontainers @@ -116,7 +117,7 @@ greenlet==2.0.1 # via sqlalchemy grpc-google-iam-v1==0.12.4 # via google-cloud-pubsub -grpcio==1.50.0 +grpcio==1.51.1 # via # google-api-core # googleapis-common-protos @@ -132,7 +133,7 @@ idna==3.4 # trio imagesize==1.4.1 # via sphinx -importlib-metadata==5.0.0 +importlib-metadata==5.1.0 # via # jsonschema # keyring @@ -166,11 +167,13 @@ markupsafe==2.1.1 # via jinja2 mccabe==0.6.1 # via flake8 +minio==7.1.12 + # via testcontainers more-itertools==9.0.0 # via jaraco-classes msrest==0.7.1 # via azure-storage-blob -neo4j==5.2.1 +neo4j==5.3.0 # via testcontainers oauthlib==3.2.2 # via requests-oauthlib @@ -189,7 +192,7 @@ pg8000==1.29.3 # via -r requirements.in pika==1.3.1 # via testcontainers -pkginfo==1.8.3 +pkginfo==1.9.2 # via twine pluggy==1.0.0 # via pytest @@ -241,7 +244,7 @@ pytest==7.2.0 # pytest-cov pytest-cov==4.0.0 # via -r requirements.in -python-arango==7.5.2 +python-arango==7.5.3 # via testcontainers python-dateutil==2.8.2 # via pg8000 @@ -297,7 +300,7 @@ scramp==1.4.4 # via pg8000 secretstorage==3.3.3 # via keyring -selenium==4.6.0 +selenium==4.7.2 # via testcontainers six==1.16.0 # via @@ -306,7 +309,6 @@ six==1.16.0 # dockerpty # ecdsa # google-auth - # grpcio # isodate # jsonschema # paramiko @@ -346,7 +348,7 @@ trio==0.22.0 # trio-websocket trio-websocket==0.9.2 # via selenium -twine==4.0.1 +twine==4.0.2 # via -r requirements.in typing-extensions==4.4.0 # via @@ -356,13 +358,14 @@ typing-extensions==4.4.0 # importlib-metadata # redis # rich -tzdata==2022.6 +tzdata==2022.7 # via pytz-deprecation-shim tzlocal==4.2 # via clickhouse-driver urllib3[socks]==1.26.13 # via # docker + # minio # python-arango # python-keycloak # requests @@ -378,7 +381,7 @@ wrapt==1.14.1 # via testcontainers wsproto==1.2.0 # via trio-websocket -zipp==3.10.0 +zipp==3.11.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/3.8.txt b/requirements/3.8.txt index adc6ffac1..3b2b5c5bd 100644 --- a/requirements/3.8.txt +++ b/requirements/3.8.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.8 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: # # pip-compile --output-file=requirements/3.8.txt --resolver=backtracking requirements.in # @@ -42,6 +42,7 @@ cachetools==5.2.0 # via google-auth certifi==2022.9.24 # via + # minio # msrest # requests # selenium @@ -51,7 +52,7 @@ cffi==1.15.1 # pynacl charset-normalizer==2.1.1 # via requests -clickhouse-driver==0.2.4 +clickhouse-driver==0.2.5 # via testcontainers codecov==2.1.12 # via -r requirements.in @@ -99,9 +100,9 @@ exceptiongroup==1.0.4 # trio flake8==3.7.9 # via -r requirements.in -google-api-core[grpc]==2.10.2 +google-api-core[grpc]==2.11.0 # via google-cloud-pubsub -google-auth==2.14.1 +google-auth==2.15.0 # via google-api-core google-cloud-pubsub==1.7.2 # via testcontainers @@ -114,7 +115,7 @@ greenlet==2.0.1 # via sqlalchemy grpc-google-iam-v1==0.12.4 # via google-cloud-pubsub -grpcio==1.50.0 +grpcio==1.51.1 # via # google-api-core # googleapis-common-protos @@ -130,7 +131,7 @@ idna==3.4 # trio imagesize==1.4.1 # via sphinx -importlib-metadata==5.0.0 +importlib-metadata==5.1.0 # via # keyring # sphinx @@ -157,11 +158,13 @@ markupsafe==2.1.1 # via jinja2 mccabe==0.6.1 # via flake8 +minio==7.1.12 + # via testcontainers more-itertools==9.0.0 # via jaraco-classes msrest==0.7.1 # via azure-storage-blob -neo4j==5.2.1 +neo4j==5.3.0 # via testcontainers oauthlib==3.2.2 # via requests-oauthlib @@ -180,7 +183,7 @@ pg8000==1.29.3 # via -r requirements.in pika==1.3.1 # via testcontainers -pkginfo==1.8.3 +pkginfo==1.9.2 # via twine pluggy==1.0.0 # via pytest @@ -232,7 +235,7 @@ pytest==7.2.0 # pytest-cov pytest-cov==4.0.0 # via -r requirements.in -python-arango==7.5.2 +python-arango==7.5.3 # via testcontainers python-dateutil==2.8.2 # via pg8000 @@ -288,7 +291,7 @@ scramp==1.4.4 # via pg8000 secretstorage==3.3.3 # via keyring -selenium==4.6.0 +selenium==4.7.2 # via testcontainers six==1.16.0 # via @@ -297,7 +300,6 @@ six==1.16.0 # dockerpty # ecdsa # google-auth - # grpcio # isodate # jsonschema # paramiko @@ -337,19 +339,20 @@ trio==0.22.0 # trio-websocket trio-websocket==0.9.2 # via selenium -twine==4.0.1 +twine==4.0.2 # via -r requirements.in typing-extensions==4.4.0 # via # azure-core # rich -tzdata==2022.6 +tzdata==2022.7 # via pytz-deprecation-shim tzlocal==4.2 # via clickhouse-driver urllib3[socks]==1.26.13 # via # docker + # minio # python-arango # python-keycloak # requests @@ -365,7 +368,7 @@ wrapt==1.14.1 # via testcontainers wsproto==1.2.0 # via trio-websocket -zipp==3.10.0 +zipp==3.11.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/3.9.txt b/requirements/3.9.txt index e43224747..84d1a9ce3 100644 --- a/requirements/3.9.txt +++ b/requirements/3.9.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # pip-compile --output-file=requirements/3.9.txt --resolver=backtracking requirements.in # @@ -38,6 +38,7 @@ cachetools==5.2.0 # via google-auth certifi==2022.9.24 # via + # minio # msrest # requests # selenium @@ -47,7 +48,7 @@ cffi==1.15.1 # pynacl charset-normalizer==2.1.1 # via requests -clickhouse-driver==0.2.4 +clickhouse-driver==0.2.5 # via testcontainers codecov==2.1.12 # via -r requirements.in @@ -95,9 +96,9 @@ exceptiongroup==1.0.4 # trio flake8==3.7.9 # via -r requirements.in -google-api-core[grpc]==2.10.2 +google-api-core[grpc]==2.11.0 # via google-cloud-pubsub -google-auth==2.14.1 +google-auth==2.15.0 # via google-api-core google-cloud-pubsub==1.7.2 # via testcontainers @@ -110,7 +111,7 @@ greenlet==2.0.1 # via sqlalchemy grpc-google-iam-v1==0.12.4 # via google-cloud-pubsub -grpcio==1.50.0 +grpcio==1.51.1 # via # google-api-core # googleapis-common-protos @@ -126,7 +127,7 @@ idna==3.4 # trio imagesize==1.4.1 # via sphinx -importlib-metadata==5.0.0 +importlib-metadata==5.1.0 # via # keyring # sphinx @@ -153,11 +154,13 @@ markupsafe==2.1.1 # via jinja2 mccabe==0.6.1 # via flake8 +minio==7.1.12 + # via testcontainers more-itertools==9.0.0 # via jaraco-classes msrest==0.7.1 # via azure-storage-blob -neo4j==5.2.1 +neo4j==5.3.0 # via testcontainers oauthlib==3.2.2 # via requests-oauthlib @@ -176,7 +179,7 @@ pg8000==1.29.3 # via -r requirements.in pika==1.3.1 # via testcontainers -pkginfo==1.8.3 +pkginfo==1.9.2 # via twine pluggy==1.0.0 # via pytest @@ -228,7 +231,7 @@ pytest==7.2.0 # pytest-cov pytest-cov==4.0.0 # via -r requirements.in -python-arango==7.5.2 +python-arango==7.5.3 # via testcontainers python-dateutil==2.8.2 # via pg8000 @@ -284,7 +287,7 @@ scramp==1.4.4 # via pg8000 secretstorage==3.3.3 # via keyring -selenium==4.6.0 +selenium==4.7.2 # via testcontainers six==1.16.0 # via @@ -293,7 +296,6 @@ six==1.16.0 # dockerpty # ecdsa # google-auth - # grpcio # isodate # jsonschema # paramiko @@ -333,17 +335,18 @@ trio==0.22.0 # trio-websocket trio-websocket==0.9.2 # via selenium -twine==4.0.1 +twine==4.0.2 # via -r requirements.in typing-extensions==4.4.0 # via azure-core -tzdata==2022.6 +tzdata==2022.7 # via pytz-deprecation-shim tzlocal==4.2 # via clickhouse-driver urllib3[socks]==1.26.13 # via # docker + # minio # python-arango # python-keycloak # requests @@ -359,7 +362,7 @@ wrapt==1.14.1 # via testcontainers wsproto==1.2.0 # via trio-websocket -zipp==3.10.0 +zipp==3.11.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/setup.py b/setup.py index 706333a2e..82bfe42d9 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,8 @@ 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Topic :: Software Development :: Libraries :: Python Modules', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', @@ -59,6 +61,7 @@ 'postgresql': ['sqlalchemy', 'psycopg2-binary'], 'selenium': ['selenium'], 'google-cloud-pubsub': ['google-cloud-pubsub < 2'], + 'minio': ['minio'], 'mongo': ['pymongo'], 'redis': ['redis'], 'mssqlserver': ['pymssql'], diff --git a/testcontainers/minio.py b/testcontainers/minio.py new file mode 100644 index 000000000..1e60f5e34 --- /dev/null +++ b/testcontainers/minio.py @@ -0,0 +1,75 @@ +from typing import TypedDict + +from minio import Minio +from requests import ConnectionError, Response, get + +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_container_is_ready + + +class MinioConfig(TypedDict): + endpoint: str + console_address: str + access_key: str + secret_key: str + + +class MinioContainer(DockerContainer): + def __init__( + self, + image="minio/minio:RELEASE.2022-12-02T19-19-22Z", + port_to_expose=9000, + access_key="minioadmin", + secret_key="minioadmin", + **kwargs, + ): + super(MinioContainer, self).__init__(image, **kwargs) + self.port_to_expose = port_to_expose + self.console_port = port_to_expose + 1 + self.access_key = access_key + self.secret_key = secret_key + + self.with_exposed_ports(self.port_to_expose, self.console_port) + self.with_env("MINIO_ACCESS_KEY", self.access_key) + self.with_env("MINIO_SECRET_KEY", self.secret_key) + self.with_command( + f"server /data --address :{self.port_to_expose} --console-address :{self.console_port}" + ) + + def get_client(self, **kwargs) -> Minio: + """Returns a Minio client to connect to the container. + + Returns: + Minio: Python Minio Client according to https://min.io/docs/minio/linux/developers/python/API.html + """ + return Minio( + f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port_to_expose)}", + access_key=self.access_key, + secret_key=self.secret_key, + secure=False, + **kwargs, + ) + + def get_config(self) -> MinioConfig: + """Returns the configuration of the Minio container. + + Returns: + MinioConfig: Dictionary with the endpoint, access_key and secret_key. + """ + return { + "endpoint": f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port_to_expose)}", + "console_address": f"http://{self.get_container_host_ip()}:{self.console_port}", + "access_key": self.access_key, + "secret_key": self.secret_key, + } + + @wait_container_is_ready(ConnectionError) + def _healthcheck(self): + url = f"http://{self.get_config()['endpoint']}/minio/health/live" + response: Response = get(url) + response.raise_for_status() + + def start(self): + super().start() + self._healthcheck() + return self diff --git a/tests/test_minio.py b/tests/test_minio.py new file mode 100644 index 000000000..8fb62d9f2 --- /dev/null +++ b/tests/test_minio.py @@ -0,0 +1,37 @@ +import io +import socket +from contextlib import closing + +from pytest import fixture + +from testcontainers.minio import MinioContainer + + +@fixture +def port_to_expose(): + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + s.bind(("localhost", 0)) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + return s.getsockname()[1] + + +def test_docker_run_minio(port_to_expose): + config = MinioContainer( + port_to_expose=port_to_expose, + access_key="test-access", + secret_key="test-secret", + ) + with config as minio: + client = minio.get_client() + client.make_bucket("test") + test_content = b"Hello World" + client.put_object( + "test", + "testfile.txt", + io.BytesIO(test_content), + length=len(test_content), + ) + + assert client.get_object("test", "testfile.txt").data == test_content + assert minio.get_config()["access_key"] == config.access_key + assert minio.get_config()["secret_key"] == config.secret_key From d87820cc8c8014919b06125205cbb61a7cf3afda Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Sun, 4 Dec 2022 09:53:51 +0100 Subject: [PATCH 02/13] make everything headless --- testcontainers/minio.py | 8 ++------ tests/test_minio.py | 20 ++------------------ 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 1e60f5e34..6a33d29b4 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -25,16 +25,13 @@ def __init__( ): super(MinioContainer, self).__init__(image, **kwargs) self.port_to_expose = port_to_expose - self.console_port = port_to_expose + 1 self.access_key = access_key self.secret_key = secret_key - self.with_exposed_ports(self.port_to_expose, self.console_port) + self.with_exposed_ports(self.port_to_expose) self.with_env("MINIO_ACCESS_KEY", self.access_key) self.with_env("MINIO_SECRET_KEY", self.secret_key) - self.with_command( - f"server /data --address :{self.port_to_expose} --console-address :{self.console_port}" - ) + self.with_command(f"server /data --address :{self.port_to_expose}") def get_client(self, **kwargs) -> Minio: """Returns a Minio client to connect to the container. @@ -58,7 +55,6 @@ def get_config(self) -> MinioConfig: """ return { "endpoint": f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port_to_expose)}", - "console_address": f"http://{self.get_container_host_ip()}:{self.console_port}", "access_key": self.access_key, "secret_key": self.secret_key, } diff --git a/tests/test_minio.py b/tests/test_minio.py index 8fb62d9f2..99eed4380 100644 --- a/tests/test_minio.py +++ b/tests/test_minio.py @@ -1,26 +1,10 @@ import io -import socket -from contextlib import closing - -from pytest import fixture from testcontainers.minio import MinioContainer -@fixture -def port_to_expose(): - with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: - s.bind(("localhost", 0)) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - return s.getsockname()[1] - - -def test_docker_run_minio(port_to_expose): - config = MinioContainer( - port_to_expose=port_to_expose, - access_key="test-access", - secret_key="test-secret", - ) +def test_docker_run_minio(): + config = MinioContainer(access_key="test-access", secret_key="test-secret") with config as minio: client = minio.get_client() client.make_bucket("test") From 6af0465ae39fdb22a17eca7ff6e6e02f949beee3 Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Sun, 4 Dec 2022 10:11:05 +0100 Subject: [PATCH 03/13] removed console_address from MinioConfig --- testcontainers/minio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 6a33d29b4..954907c48 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -9,7 +9,6 @@ class MinioConfig(TypedDict): endpoint: str - console_address: str access_key: str secret_key: str From 59cca7011538397ce95a5faa06b6847625eef45e Mon Sep 17 00:00:00 2001 From: Malte Hedderich <12952192+maltehedderich@users.noreply.github.com> Date: Mon, 5 Dec 2022 16:42:38 +0100 Subject: [PATCH 04/13] Reduce line length to <=100 --- testcontainers/minio.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 954907c48..32ec64d5e 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -36,7 +36,8 @@ def get_client(self, **kwargs) -> Minio: """Returns a Minio client to connect to the container. Returns: - Minio: Python Minio Client according to https://min.io/docs/minio/linux/developers/python/API.html + Minio: Python Minio Client according to + https://min.io/docs/minio/linux/developers/python/API.html """ return Minio( f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port_to_expose)}", @@ -53,7 +54,8 @@ def get_config(self) -> MinioConfig: MinioConfig: Dictionary with the endpoint, access_key and secret_key. """ return { - "endpoint": f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port_to_expose)}", + "endpoint": f"{self.get_container_host_ip()}" + + f":{self.get_exposed_port(self.port_to_expose)}", "access_key": self.access_key, "secret_key": self.secret_key, } From f5b07c15916576ede241e232dce470f00a9a7e53 Mon Sep 17 00:00:00 2001 From: Malte Hedderich <12952192+maltehedderich@users.noreply.github.com> Date: Mon, 5 Dec 2022 17:02:37 +0100 Subject: [PATCH 05/13] Fix flake8 W291 and E131 --- testcontainers/minio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 32ec64d5e..385795c82 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -36,7 +36,7 @@ def get_client(self, **kwargs) -> Minio: """Returns a Minio client to connect to the container. Returns: - Minio: Python Minio Client according to + Minio: Python Minio Client according to https://min.io/docs/minio/linux/developers/python/API.html """ return Minio( @@ -55,7 +55,7 @@ def get_config(self) -> MinioConfig: """ return { "endpoint": f"{self.get_container_host_ip()}" + - f":{self.get_exposed_port(self.port_to_expose)}", + f":{self.get_exposed_port(self.port_to_expose)}", "access_key": self.access_key, "secret_key": self.secret_key, } From a50f4d9987317ab223a721d5f90902451d8d8321 Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 20:50:22 +0100 Subject: [PATCH 06/13] remove TypedDict for py3.7 compatibility --- testcontainers/minio.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 385795c82..21f7b25cd 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -1,5 +1,3 @@ -from typing import TypedDict - from minio import Minio from requests import ConnectionError, Response, get @@ -7,12 +5,6 @@ from testcontainers.core.waiting_utils import wait_container_is_ready -class MinioConfig(TypedDict): - endpoint: str - access_key: str - secret_key: str - - class MinioContainer(DockerContainer): def __init__( self, @@ -37,25 +29,28 @@ def get_client(self, **kwargs) -> Minio: Returns: Minio: Python Minio Client according to - https://min.io/docs/minio/linux/developers/python/API.html + https://min.io/docs/minio/linux/developers/python/API.html """ + host_ip = self.get_container_host_ip() + exposed_port = self.get_exposed_port(self.port_to_expose) return Minio( - f"{self.get_container_host_ip()}:{self.get_exposed_port(self.port_to_expose)}", + f"{host_ip}:{exposed_port}", access_key=self.access_key, secret_key=self.secret_key, secure=False, **kwargs, ) - def get_config(self) -> MinioConfig: + def get_config(self) -> dict: """Returns the configuration of the Minio container. Returns: MinioConfig: Dictionary with the endpoint, access_key and secret_key. """ + host_ip = self.get_container_host_ip() + exposed_port = self.get_exposed_port(self.port_to_expose) return { - "endpoint": f"{self.get_container_host_ip()}" + - f":{self.get_exposed_port(self.port_to_expose)}", + "endpoint": f"{host_ip}:{exposed_port}", "access_key": self.access_key, "secret_key": self.secret_key, } From 31fd13721482dd1a0b1914084dba6df11bcecda8 Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 21:07:00 +0100 Subject: [PATCH 07/13] improved docstring for getconfig --- testcontainers/minio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 21f7b25cd..8da3b8b5e 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -45,7 +45,7 @@ def get_config(self) -> dict: """Returns the configuration of the Minio container. Returns: - MinioConfig: Dictionary with the endpoint, access_key and secret_key. + dict: {`endpoint`: str, `access_key`: str, `secret_key`: str} """ host_ip = self.get_container_host_ip() exposed_port = self.get_exposed_port(self.port_to_expose) From 16cbdcdc57a3ebb379af7549809ff81444d44863 Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 22:21:11 +0100 Subject: [PATCH 08/13] Add documentation and doctest --- testcontainers/minio.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 8da3b8b5e..0f7990305 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -6,6 +6,32 @@ class MinioContainer(DockerContainer): + """ + The example below spins up an Minio container and creates a new bucket in it. + Furthermore, it demonstrates how an object is written to this bucket and then subsequently retrieved. + The method :code:`get_client` can be used to create a client for the Minio Python API. + The method :code:`get_config` can be used to retrieve the endpoint, access key + and secret key of the container. + + Example + ------- + .. doctest:: + + >>> from testcontainers.minio import MinioContainer + + >>> with MinioContainer() as minio: + ... client = minio.get_client() + ... client.make_bucket("test") + ... test_content = b"Hello World" + ... client.put_object( + ... "test", + ... "testfile.txt", + ... io.BytesIO(test_content), + ... length=len(test_content), + ... ) + ... retrieved_content = client.get_object("test", "testfile.txt").data + """ + def __init__( self, image="minio/minio:RELEASE.2022-12-02T19-19-22Z", @@ -14,6 +40,14 @@ def __init__( secret_key="minioadmin", **kwargs, ): + """ + Args: + image (str, optional): The Docker image to use for the Minio container. + Defaults to "minio/minio:RELEASE.2022-12-02T19-19-22Z". + port_to_expose (int, optional): The port to expose on the container. Defaults to 9000. + access_key (str, optional): The access key for client connections. Defaults to "minioadmin". + secret_key (str, optional): The secret key for client connections. Defaults to "minioadmin". + """ super(MinioContainer, self).__init__(image, **kwargs) self.port_to_expose = port_to_expose self.access_key = access_key @@ -42,7 +76,8 @@ def get_client(self, **kwargs) -> Minio: ) def get_config(self) -> dict: - """Returns the configuration of the Minio container. + """This method returns the configuration of the Minio container, + including the endpoint, access key, and secret key. Returns: dict: {`endpoint`: str, `access_key`: str, `secret_key`: str} @@ -57,11 +92,15 @@ def get_config(self) -> dict: @wait_container_is_ready(ConnectionError) def _healthcheck(self): + """This is an internal method used to check if the Minio container + is healthy and ready to receive requests.""" url = f"http://{self.get_config()['endpoint']}/minio/health/live" response: Response = get(url) response.raise_for_status() def start(self): + """This method starts the Minio container and runs the healthcheck + to verify that the container is ready to use.""" super().start() self._healthcheck() return self From 50f95a3bf3a6294bad350018c8c8a28ff80e375e Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 22:28:57 +0100 Subject: [PATCH 09/13] Line length <100 --- testcontainers/minio.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 0f7990305..28ae55c75 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -8,7 +8,8 @@ class MinioContainer(DockerContainer): """ The example below spins up an Minio container and creates a new bucket in it. - Furthermore, it demonstrates how an object is written to this bucket and then subsequently retrieved. + Furthermore, it demonstrates how an object is written to this bucket and + then subsequently retrieved. The method :code:`get_client` can be used to create a client for the Minio Python API. The method :code:`get_config` can be used to retrieve the endpoint, access key and secret key of the container. @@ -44,9 +45,12 @@ def __init__( Args: image (str, optional): The Docker image to use for the Minio container. Defaults to "minio/minio:RELEASE.2022-12-02T19-19-22Z". - port_to_expose (int, optional): The port to expose on the container. Defaults to 9000. - access_key (str, optional): The access key for client connections. Defaults to "minioadmin". - secret_key (str, optional): The secret key for client connections. Defaults to "minioadmin". + port_to_expose (int, optional): The port to expose on the container. + Defaults to 9000. + access_key (str, optional): The access key for client connections. + Defaults to "minioadmin". + secret_key (str, optional): The secret key for client connections. + Defaults to "minioadmin". """ super(MinioContainer, self).__init__(image, **kwargs) self.port_to_expose = port_to_expose From 77242ae74c4417ebdc112eaddafb2fe13b5f3de8 Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 22:44:58 +0100 Subject: [PATCH 10/13] add reference in docs --- docs/index.rst | 1 + docs/minio.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 docs/minio.rst diff --git a/docs/index.rst b/docs/index.rst index 8cdd90ce8..8404eb6da 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,4 +22,5 @@ Usage modes Keycloak container RabbitMQ container Redis container + Minio container diff --git a/docs/minio.rst b/docs/minio.rst new file mode 100644 index 000000000..d11916a3d --- /dev/null +++ b/docs/minio.rst @@ -0,0 +1,6 @@ +Minio +=================== + +Allows to spin up Minio Container. + +.. autoclass:: testcontainers.minio.MinioContainer From c79c2dc8bd84fff7fb68b9fa8aa6a589ceaa04d6 Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 22:50:17 +0100 Subject: [PATCH 11/13] added minio container to readme --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index c8a7aa3e9..488291985 100644 --- a/README.rst +++ b/README.rst @@ -27,6 +27,7 @@ Currently available features: * RabbitMQ * Keycloak * Azurite container +* Minio container Installation ------------ From c7e8a6f6f4fb5363df8b92360393cd413448b8e7 Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 22:51:41 +0100 Subject: [PATCH 12/13] fixed missing io import in doctest --- testcontainers/minio.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index 28ae55c75..ab2bde3ae 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -18,6 +18,7 @@ class MinioContainer(DockerContainer): ------- .. doctest:: + >>> import io >>> from testcontainers.minio import MinioContainer >>> with MinioContainer() as minio: From cc6cf7b1e3b79fc39bed07d16a2eace71a292ece Mon Sep 17 00:00:00 2001 From: Malte Hedderich Date: Tue, 6 Dec 2022 23:04:18 +0100 Subject: [PATCH 13/13] stored ObjectWriteResult --- testcontainers/minio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcontainers/minio.py b/testcontainers/minio.py index ab2bde3ae..039cff647 100644 --- a/testcontainers/minio.py +++ b/testcontainers/minio.py @@ -25,7 +25,7 @@ class MinioContainer(DockerContainer): ... client = minio.get_client() ... client.make_bucket("test") ... test_content = b"Hello World" - ... client.put_object( + ... write_result = client.put_object( ... "test", ... "testfile.txt", ... io.BytesIO(test_content),