Skip to content

Commit 85f7451

Browse files
committed
Added a new Docker registry test container and tests
1 parent 928af5a commit 85f7451

File tree

7 files changed

+109
-0
lines changed

7 files changed

+109
-0
lines changed

.github/workflows/main.yml

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
- rabbitmq
4545
- redis
4646
- selenium
47+
- registry
4748
runs-on: ${{ matrix.runtime.machine }}
4849
steps:
4950
- uses: actions/checkout@v3

README.rst

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ testcontainers-python facilitates the use of Docker containers for functional an
3434
rabbitmq/README
3535
redis/README
3636
selenium/README
37+
registry/README
3738

3839
Getting Started
3940
---------------

registry/README.rst

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. autoclass:: testcontainers.registry.DockerRegistryContainer
2+

registry/setup.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from setuptools import setup, find_namespace_packages
2+
3+
description = "Redis component of testcontainers-python."
4+
5+
setup(
6+
name="testcontainers-registry",
7+
version="0.0.1rc1",
8+
packages=find_namespace_packages(),
9+
description=description,
10+
long_description=description,
11+
long_description_content_type="text/x-rst",
12+
url="https://github.com/testcontainers/testcontainers-python",
13+
install_requires=[
14+
"testcontainers-core",
15+
"bcrypt",
16+
],
17+
python_requires=">=3.7",
18+
)

registry/testcontainers/__init__.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import time
2+
from io import BytesIO
3+
from tarfile import TarFile, TarInfo
4+
from typing import Optional
5+
6+
import bcrypt
7+
from testcontainers.core.container import DockerContainer
8+
9+
class DockerRegistryContainer(DockerContainer):
10+
# https://docs.docker.com/registry/
11+
credentials_path: str = "/htpasswd/credentials.txt"
12+
13+
def __init__(
14+
self,
15+
image: str = "registry:latest",
16+
port: int = 5000,
17+
username: str = None,
18+
password: str = None,
19+
**kwargs,
20+
) -> None:
21+
super().__init__(image=image, **kwargs)
22+
self.port: int = port
23+
self.username: Optional[str] = username
24+
self.password: Optional[str] = password
25+
self.with_exposed_ports(self.port)
26+
27+
def _copy_credentials(self):
28+
# Create credentials and write them to the container
29+
hashed_password: str = bcrypt.hashpw(
30+
self.password.encode("utf-8"),
31+
bcrypt.gensalt(rounds=12, prefix=b"2a"),
32+
).decode("utf-8")
33+
content = f"{self.username}:{hashed_password}".encode("utf-8")
34+
35+
with BytesIO() as tar_archive_object, TarFile(
36+
fileobj=tar_archive_object, mode="w"
37+
) as tmp_tarfile:
38+
tarinfo: TarInfo = TarInfo(name=self.credentials_path)
39+
tarinfo.size = len(content)
40+
tarinfo.mtime = time.time()
41+
42+
tmp_tarfile.addfile(tarinfo, BytesIO(content))
43+
tar_archive_object.seek(0)
44+
self.get_wrapped_container().put_archive("/", tar_archive_object)
45+
46+
def start(self):
47+
if self.username and self.password:
48+
self.with_env("REGISTRY_AUTH_HTPASSWD_REALM", "local-registry")
49+
self.with_env("REGISTRY_AUTH_HTPASSWD_PATH", self.credentials_path)
50+
super().start()
51+
self._copy_credentials()
52+
return self
53+
else:
54+
super().start()
55+
return self
56+
57+
def get_registry(self) -> str:
58+
host: str = self.get_container_host_ip()
59+
port: str = self.get_exposed_port(self.port)
60+
return f"{host}:{port}"

registry/tests/test_registry.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from requests import Response, get
2+
from requests.auth import HTTPBasicAuth
3+
from testcontainers.registry import DockerRegistryContainer
4+
5+
6+
REGISTRY_USERNAME: str = "foo"
7+
REGISTRY_PASSWORD: str ="bar"
8+
9+
def test_registry():
10+
with DockerRegistryContainer().with_bind_ports(5000, 5000) as registry_container:
11+
url: str = f"http://{registry_container.get_registry()}/v2/_catalog"
12+
13+
response: Response = get(url)
14+
15+
assert response.status_code == 200
16+
17+
18+
def test_registry_with_authentication():
19+
with DockerRegistryContainer(
20+
username=REGISTRY_USERNAME, password=REGISTRY_PASSWORD
21+
).with_bind_ports(5000, 5000) as registry_container:
22+
url: str = f"http://{registry_container.get_registry()}/v2/_catalog"
23+
24+
response: Response = get(url, auth=HTTPBasicAuth(REGISTRY_USERNAME, REGISTRY_PASSWORD))
25+
26+
assert response.status_code == 200

requirements.in

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
-e file:rabbitmq
2222
-e file:redis
2323
-e file:selenium
24+
-e file:registry
2425
cryptography<37
2526
flake8<3.8.0 # 3.8.0 adds a dependency on importlib-metadata which conflicts with other packages.
2627
pg8000

0 commit comments

Comments
 (0)