1010#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
1111#    License for the specific language governing permissions and limitations 
1212#    under the License. 
13+ import  contextlib 
1314import  functools  as  ft 
1415import  importlib .metadata 
1516import  ipaddress 
1617import  os 
18+ import  socket 
1719import  urllib 
1820import  urllib .parse 
1921from  collections .abc  import  Iterable 
2426from  docker .models .images  import  Image , ImageCollection 
2527from  typing_extensions  import  ParamSpec 
2628
29+ from  testcontainers .core  import  utils 
2730from  testcontainers .core .auth  import  DockerAuthInfo , parse_docker_auth_config 
31+ from  testcontainers .core .config  import  ConnectionMode 
2832from  testcontainers .core .config  import  testcontainers_config  as  c 
2933from  testcontainers .core .labels  import  SESSION_ID , create_labels 
30- from  testcontainers .core .utils  import  default_gateway_ip , inside_container , setup_logger 
3134
32- LOGGER  =  setup_logger (__name__ )
35+ LOGGER  =  utils . setup_logger (__name__ )
3336
3437_P  =  ParamSpec ("_P" )
3538_T  =  TypeVar ("_T" )
@@ -127,8 +130,18 @@ def find_host_network(self) -> Optional[str]:
127130        """ 
128131        # If we're docker in docker running on a custom network, we need to inherit the 
129132        # network settings, so we can access the resulting container. 
133+ 
134+         # first to try to find the network the container runs in, if we can determine 
135+         container_id  =  utils .get_running_in_container_id ()
136+         if  container_id :
137+             with  contextlib .suppress (Exception ):
138+                 return  self .network_name (container_id )
139+ 
140+         # if this results nothing, try to determine the network based on the 
141+         # docker_host 
130142        try :
131-             docker_host  =  ipaddress .IPv4Address (self .host ())
143+             host_ip  =  socket .gethostbyname (self .host ())
144+             docker_host  =  ipaddress .IPv4Address (host_ip )
132145            # See if we can find the host on our networks 
133146            for  network  in  self .client .networks .list (filters = {"type" : "custom" }):
134147                if  "IPAM"  in  network .attrs :
@@ -139,7 +152,7 @@ def find_host_network(self) -> Optional[str]:
139152                            continue 
140153                        if  docker_host  in  subnet :
141154                            return  network .name 
142-         except  ipaddress .AddressValueError :
155+         except  ( ipaddress .AddressValueError ,  OSError ) :
143156            pass 
144157        return  None 
145158
@@ -187,6 +200,28 @@ def gateway_ip(self, container_id: str) -> str:
187200        network_name  =  self .network_name (container_id )
188201        return  container ["NetworkSettings" ]["Networks" ][network_name ]["Gateway" ]
189202
203+     def  get_connection_mode (self ) ->  ConnectionMode :
204+         """ 
205+         Determine the connection mode. 
206+ 
207+         See https://github.com/testcontainers/testcontainers-python/issues/475#issuecomment-2407250970 
208+         """ 
209+         if  c .connection_mode_override :
210+             return  c .connection_mode_override 
211+         localhosts  =  {"localhost" , "127.0.0.1" , "::1" }
212+         if  not  utils .inside_container () or  self .host () not  in localhosts :
213+             # if running not inside a container or with a non-local docker client, 
214+             # connect ot the docker host per default 
215+             return  ConnectionMode .docker_host 
216+         elif  self .find_host_network ():
217+             # a host network could be determined, indicator for DooD, 
218+             # so we should connect to the bridge_ip as the container we run in 
219+             # and the one we started are connected to the same network 
220+             # that might have no access to either docker_host or the gateway 
221+             return  ConnectionMode .bridge_ip 
222+         # default for DinD 
223+         return  ConnectionMode .gateway_ip 
224+ 
190225    def  host (self ) ->  str :
191226        """ 
192227        Get the hostname or ip address of the docker host. 
@@ -196,13 +231,15 @@ def host(self) -> str:
196231            return  host 
197232        try :
198233            url  =  urllib .parse .urlparse (self .client .api .base_url )
199- 
200234        except  ValueError :
201235            return  "localhost" 
202-         if  "http"  in  url .scheme  or  "tcp"  in  url .scheme :
236+         if  "http"  in  url .scheme  or  "tcp"  in  url .scheme  and  url .hostname :
237+             # see https://github.com/testcontainers/testcontainers-python/issues/415 
238+             if  url .hostname  ==  "localnpipe"  and  utils .is_windows ():
239+                 return  "localhost" 
203240            return  url .hostname 
204-         if  inside_container () and  ("unix"  in  url .scheme  or  "npipe"  in  url .scheme ):
205-             ip_address  =  default_gateway_ip ()
241+         if  utils . inside_container () and  ("unix"  in  url .scheme  or  "npipe"  in  url .scheme ):
242+             ip_address  =  utils . default_gateway_ip ()
206243            if  ip_address :
207244                return  ip_address 
208245        return  "localhost" 
0 commit comments