1+ import base64
2+ import json
3+
4+ import pytest
15from requests import Response , get
26from requests .auth import HTTPBasicAuth
3- from testcontainers .registry import DockerRegistryContainer
47
8+ from testcontainers .core .config import testcontainers_config
9+ from testcontainers .core .docker_client import DockerClient
10+ from testcontainers .core .utils import is_mac
11+ from testcontainers .registry import DockerRegistryContainer
512
613REGISTRY_USERNAME : str = "foo"
714REGISTRY_PASSWORD : str = "bar"
@@ -25,3 +32,56 @@ def test_registry_with_authentication() -> None:
2532 response : Response = get (url , auth = HTTPBasicAuth (REGISTRY_USERNAME , REGISTRY_PASSWORD ))
2633
2734 assert response .status_code == 200
35+
36+
37+ @pytest .mark .skipif (
38+ is_mac (),
39+ reason = "Docker Desktop on macOS does not support insecure private registries without daemon reconfiguration" ,
40+ )
41+ def test_registry_login (monkeypatch ) -> None :
42+ """DockerClient auto-logs-in via DOCKER_AUTH_CONFIG, proven by pulling a private image.
43+
44+ Works against remote (SSH) Docker hosts too: the registry is addressed via
45+ ``127.0.0.1:<published_port>``, which the daemon treats as insecure-by-default
46+ (the ``127.0.0.0/8`` range), so the login/push/pull happen over plain HTTP
47+ without reconfiguring the daemon's ``insecure-registries``.
48+ """
49+ image : str = "hello-world"
50+ tag : str = "latest"
51+
52+ with DockerRegistryContainer (username = REGISTRY_USERNAME , password = REGISTRY_PASSWORD ) as registry_container :
53+ # Address the registry over the daemon's loopback (works for both local
54+ # and remote/SSH daemons) so HTTP is allowed without insecure-registries.
55+ port : str = registry_container .get_exposed_port (registry_container .port )
56+ registry_url : str = f"127.0.0.1:{ port } "
57+ private_ref : str = f"{ registry_url } /{ image } :{ tag } "
58+
59+ creds : str = base64 .b64encode (f"{ REGISTRY_USERNAME } :{ REGISTRY_PASSWORD } " .encode ()).decode ()
60+ auth_config : str = json .dumps ({"auths" : {registry_url : {"auth" : creds }}})
61+
62+ # Seed the private registry with an image (push authenticated explicitly),
63+ # then drop the local tag so the later pull must hit the registry. Reuse
64+ # the container's own client (it sets ``use_ssh_client`` for SSH hosts,
65+ # avoiding paramiko's stdin clash with pytest output capture).
66+ seed_client = registry_container .get_docker_client ().client
67+ seed_image = seed_client .images .pull (image , tag = tag )
68+ seed_image .tag (f"{ registry_url } /{ image } " , tag = tag )
69+ seed_client .images .push (
70+ f"{ registry_url } /{ image } " ,
71+ tag = tag ,
72+ auth_config = {"username" : REGISTRY_USERNAME , "password" : REGISTRY_PASSWORD },
73+ )
74+ seed_client .images .remove (private_ref )
75+
76+ # DockerClient.__init__ logs in using the auth config below. Pulling the
77+ # private image without explicit credentials only succeeds if that
78+ # auto-login actually happened, so this exercises testcontainers' wiring
79+ # rather than docker-py's login() directly.
80+ monkeypatch .setattr (testcontainers_config , "docker_auth_config" , auth_config )
81+ client = DockerClient ()
82+ try :
83+ pulled = client .client .images .pull (private_ref )
84+ assert pulled is not None
85+ finally :
86+ client .client .images .remove (private_ref )
87+ client .client .close ()
0 commit comments