Skip to content

Commit 1a29616

Browse files
authored
fix: pass Docker Hub credentials for images without a registry prefix (#935)
1 parent a22fa50 commit 1a29616

1 file changed

Lines changed: 61 additions & 1 deletion

File tree

testcontainers/src/core/client.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ impl Client {
740740

741741
async fn credentials_for_image(&self, descriptor: &str) -> Option<DockerCredentials> {
742742
let auth_config = self.config.docker_auth_config()?.to_string();
743-
let (server, _) = descriptor.split_once('/')?;
743+
let server = resolve_registry(descriptor);
744744

745745
// `docker_credential` uses blocking API, thus we spawn blocking task to prevent executor from being blocked
746746
let cloned_server = server.to_string();
@@ -862,6 +862,25 @@ where
862862
}
863863
}
864864

865+
/// Docker Hub's canonical registry hostname, used for credential lookups when
866+
/// an image descriptor has no explicit registry prefix (e.g. `postgres:16`).
867+
const DOCKER_HUB_REGISTRY: &str = "index.docker.io";
868+
869+
/// Extracts the registry server from a Docker image descriptor.
870+
///
871+
/// Docker image descriptors follow the pattern `[registry/][namespace/]name[:tag]`.
872+
/// When no explicit registry is present, images are pulled from Docker Hub.
873+
///
874+
/// The first path component is treated as a registry hostname only if it contains
875+
/// a `.` or a `:` (port), matching the Docker CLI's resolution logic. Otherwise
876+
/// it is a Docker Hub namespace (e.g. `library`, `confluentinc`).
877+
fn resolve_registry(descriptor: &str) -> &str {
878+
match descriptor.split_once('/') {
879+
Some((first, _)) if first.contains('.') || first.contains(':') => first,
880+
_ => DOCKER_HUB_REGISTRY,
881+
}
882+
}
883+
865884
#[cfg(test)]
866885
mod tests {
867886
use bollard::query_parameters::RemoveImageOptions;
@@ -937,4 +956,45 @@ mod tests {
937956

938957
Ok(())
939958
}
959+
960+
#[test]
961+
fn test_resolve_registry_docker_hub_library_image() {
962+
assert_eq!(resolve_registry("postgres:16"), DOCKER_HUB_REGISTRY);
963+
assert_eq!(resolve_registry("redis:7-alpine"), DOCKER_HUB_REGISTRY);
964+
assert_eq!(resolve_registry("hello-world:linux"), DOCKER_HUB_REGISTRY);
965+
assert_eq!(resolve_registry("ubuntu"), DOCKER_HUB_REGISTRY);
966+
}
967+
968+
#[test]
969+
fn test_resolve_registry_docker_hub_namespaced_image() {
970+
assert_eq!(
971+
resolve_registry("confluentinc/cp-kafka:6.1.1"),
972+
DOCKER_HUB_REGISTRY
973+
);
974+
assert_eq!(resolve_registry("library/postgres:16"), DOCKER_HUB_REGISTRY);
975+
assert_eq!(resolve_registry("minio/minio:latest"), DOCKER_HUB_REGISTRY);
976+
}
977+
978+
#[test]
979+
fn test_resolve_registry_private_registry() {
980+
assert_eq!(resolve_registry("ghcr.io/myorg/myimage:latest"), "ghcr.io");
981+
assert_eq!(resolve_registry("quay.io/lakekeeper/catalog:v1"), "quay.io");
982+
assert_eq!(
983+
resolve_registry("registry.example.com/app:1.0"),
984+
"registry.example.com"
985+
);
986+
assert_eq!(
987+
resolve_registry("my-registry.io:5000/image:tag"),
988+
"my-registry.io:5000"
989+
);
990+
}
991+
992+
#[test]
993+
fn test_resolve_registry_with_digest() {
994+
assert_eq!(
995+
resolve_registry("postgres@sha256:abcdef1234567890"),
996+
DOCKER_HUB_REGISTRY
997+
);
998+
assert_eq!(resolve_registry("ghcr.io/org/img@sha256:abcdef"), "ghcr.io");
999+
}
9401000
}

0 commit comments

Comments
 (0)