10
10
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
11
# License for the specific language governing permissions and limitations
12
12
# under the License.
13
+ import enum
13
14
import os
14
15
import socket
15
16
from typing import Optional
19
20
from testcontainers .core .waiting_utils import wait_container_is_ready
20
21
21
22
23
+ class ConnectionStringType (enum .Enum ):
24
+ """
25
+ Enumeration for specifying the type of connection string to generate for Azurite.
26
+
27
+ :cvar LOCALHOST: Represents a connection string for access from the host machine
28
+ where the tests are running.
29
+ :cvar NETWORK: Represents a connection string for access from another container
30
+ within the same Docker network as the Azurite container.
31
+ """
32
+
33
+ LOCALHOST = "localhost"
34
+ NETWORK = "network"
35
+
36
+
22
37
class AzuriteContainer (DockerContainer ):
23
38
"""
24
39
The example below spins up an Azurite container and
@@ -73,7 +88,46 @@ def __init__(
73
88
self .with_exposed_ports (blob_service_port , queue_service_port , table_service_port )
74
89
self .with_env ("AZURITE_ACCOUNTS" , f"{ self .account_name } :{ self .account_key } " )
75
90
76
- def get_connection_string (self ) -> str :
91
+ def get_connection_string (
92
+ self , connection_string_type : ConnectionStringType = ConnectionStringType .LOCALHOST
93
+ ) -> str :
94
+ """Retrieves the appropriate connection string for the Azurite container based on the specified access type.
95
+
96
+ This method acts as a dispatcher, returning a connection string optimized
97
+ either for access from the host machine or for inter-container communication within the same Docker network.
98
+
99
+ :param connection_string_type: The type of connection string to generate.
100
+ Use :attr:`ConnectionStringType.LOCALHOST` for connections
101
+ from the machine running the tests (default), or
102
+ :attr:`ConnectionStringType.NETWORK` for connections
103
+ from other containers within the same Docker network.
104
+ :type connection_string_type: ConnectionStringType
105
+ :return: The generated Azurite connection string.
106
+ :rtype: str
107
+ :raises ValueError: If an unrecognized `connection_string_type` is provided.
108
+ """
109
+ match connection_string_type :
110
+ case ConnectionStringType .LOCALHOST :
111
+ return self .__get_local_connection_string ()
112
+ case ConnectionStringType .NETWORK :
113
+ return self .__get_external_connection_string ()
114
+ case _:
115
+ raise ValueError (
116
+ f"unrecognized connection string type { connection_string_type } , "
117
+ f"Supported values are ConnectionStringType.LOCALHOST or ConnectionStringType.NETWORK "
118
+ )
119
+
120
+ def __get_local_connection_string (self ) -> str :
121
+ """Generates a connection string for Azurite accessible from the local host machine.
122
+
123
+ This connection string uses the Docker host IP address (obtained via
124
+ :meth:`testcontainers.core.container.DockerContainer.get_container_host_ip`)
125
+ and the dynamically exposed ports of the Azurite container. This ensures that
126
+ clients running on the host can connect successfully to the Azurite services.
127
+
128
+ :return: The Azurite connection string for local host access.
129
+ :rtype: str
130
+ """
77
131
host_ip = self .get_container_host_ip ()
78
132
connection_string = (
79
133
f"DefaultEndpointsProtocol=http;AccountName={ self .account_name } ;AccountKey={ self .account_key } ;"
@@ -96,6 +150,75 @@ def get_connection_string(self) -> str:
96
150
97
151
return connection_string
98
152
153
+ def __get_external_connection_string (self ) -> str :
154
+ """Generates a connection string for Azurite, primarily optimized for
155
+ inter-container communication within a custom Docker network.
156
+
157
+ This method attempts to provide the most suitable connection string
158
+ based on the container's network configuration:
159
+
160
+ - **For Inter-Container Communication (Recommended):** If the Azurite container is
161
+ part of a custom Docker network and has network aliases configured,
162
+ the connection string will use the first network alias as the hostname
163
+ and the internal container ports (e.g., #$#`http://<alias>:<internal_port>/<account_name>`#$#).
164
+ This is the most efficient and robust way for other containers
165
+ in the same network to connect to Azurite, leveraging Docker's internal DNS.
166
+
167
+ - **Fallback for Non-Networked/Aliased Scenarios:** If the container is
168
+ not on a custom network with aliases (e.g., running on the default
169
+ bridge network without explicit aliases), the method falls back to
170
+ using the Docker host IP (obtained via
171
+ :meth:`testcontainers.core.container.DockerContainer.get_container_host_ip`)
172
+ and the dynamically exposed ports (e.g., #$#`http://<host_ip>:<exposed_port>/<account_name>`#$#).
173
+ While this connection string is technically "external" to the container,
174
+ it primarily facilitates connections *from the host machine*.
175
+
176
+ :return: The generated Azurite connection string.
177
+ :rtype: str
178
+ """
179
+ # Check if we're on a custom network and have network aliases
180
+ if hasattr (self , "_network" ) and self ._network and hasattr (self , "_network_aliases" ) and self ._network_aliases :
181
+ # Use the first network alias for inter-container communication
182
+ host_ip = self ._network_aliases [0 ]
183
+ # When using network aliases, use the internal container ports
184
+ blob_port = self .blob_service_port
185
+ queue_port = self .queue_service_port
186
+ table_port = self .table_service_port
187
+ else :
188
+ # Use the Docker host IP for external connections
189
+ host_ip = self .get_container_host_ip ()
190
+ # When using host IP, use the exposed ports
191
+ blob_port = (
192
+ self .get_exposed_port (self .blob_service_port )
193
+ if self .blob_service_port in self .ports
194
+ else self .blob_service_port
195
+ )
196
+ queue_port = (
197
+ self .get_exposed_port (self .queue_service_port )
198
+ if self .queue_service_port in self .ports
199
+ else self .queue_service_port
200
+ )
201
+ table_port = (
202
+ self .get_exposed_port (self .table_service_port )
203
+ if self .table_service_port in self .ports
204
+ else self .table_service_port
205
+ )
206
+
207
+ connection_string = (
208
+ f"DefaultEndpointsProtocol=http;AccountName={ self .account_name } ;AccountKey={ self .account_key } ;"
209
+ )
210
+
211
+ if self .blob_service_port in self .ports :
212
+ connection_string += f"BlobEndpoint=http://{ host_ip } :{ blob_port } /{ self .account_name } ;"
213
+
214
+ if self .queue_service_port in self .ports :
215
+ connection_string += f"QueueEndpoint=http://{ host_ip } :{ queue_port } /{ self .account_name } ;"
216
+
217
+ if self .table_service_port in self .ports :
218
+ connection_string += f"TableEndpoint=http://{ host_ip } :{ table_port } /{ self .account_name } ;"
219
+
220
+ return connection_string
221
+
99
222
def start (self ) -> "AzuriteContainer" :
100
223
super ().start ()
101
224
self ._connect ()
0 commit comments