@@ -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) ]
866885mod 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