Skip to content

Commit 85e2847

Browse files
authored
[chore] windows support (kind of) (#155)
Support for windows and environments where localhost is not the correct hostname now uses docker host as found by docker context
1 parent 3f8aba5 commit 85e2847

4 files changed

Lines changed: 85 additions & 24 deletions

File tree

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,33 @@ Testcontainers use the standard Logger, see https://hexdocs.pm/logger/Logger.htm
135135

136136
For more detailed information about the API, different container configurations, and advanced usage scenarios, please refer to the [API documentation](https://hexdocs.pm/testcontainers/api-reference.html).
137137

138+
## Windows support
139+
140+
### Native
141+
You can run on windows natively with elixir and erlang.
142+
143+
First install Visual Studio 2022 with Desktop development with C++.
144+
145+
Open visual studio dev shell. I do it by just opening an empty c++ project, then View -> Terminal.
146+
147+
Enable "Expose daemon on tcp://localhost:2375 without TLS" in Docker settings.
148+
149+
for powershell:
150+
151+
`$Env:DOCKER_HOST = "tcp://localhost:2375"`
152+
153+
for cmd:
154+
155+
`set DOCKER_HOST=tcp://localhost:2375`
156+
157+
Compile and run tests:
158+
159+
`mix deps.get`
160+
161+
`mix deps.compile`
162+
163+
`mix test`
164+
138165
## Contributing
139166

140167
We welcome your contributions! Please see our contributing guidelines (TBD) for more details on how to submit patches and the contribution workflow.

lib/container.ex

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,22 @@ defmodule Testcontainers.Container do
4646
defguard is_os(name)
4747
when is_atom(name) and name == @os_type
4848

49+
@dialyzer {:nowarn_function, os_type: 0}
50+
def os_type() do
51+
cond do
52+
is_os(:linux) -> :linux
53+
is_os(:macos) -> :macos
54+
is_os(:windows) -> :windows
55+
true -> :unknown
56+
end
57+
end
58+
4959
@doc """
5060
A constructor function to make it easier to construct a container
5161
"""
5262
def new(image) when is_binary(image) do
5363
%__MODULE__{image: image}
5464
end
55-
5665
@doc """
5766
Sets a _waiting strategy_ for the _container_.
5867
"""

lib/testcontainers.ex

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defmodule Testcontainers do
1818
alias Testcontainers.Util.PropertiesParser
1919

2020
import Testcontainers.Constants
21+
import Testcontainers.Container, only: [os_type: 0]
2122

2223
@timeout 300_000
2324

@@ -42,28 +43,16 @@ defmodule Testcontainers do
4243
ryuk_config =
4344
Container.new("testcontainers/ryuk:#{Constants.ryuk_version()}")
4445
|> Container.with_exposed_port(8080)
45-
|> then(fn config ->
46-
with %URI{scheme: "unix", path: docker_socket_path} <- URI.parse(docker_host) do
47-
Container.with_bind_mount(
48-
config,
49-
docker_socket_path,
50-
"/var/run/docker.sock",
51-
"rw"
52-
)
53-
else
54-
_ ->
55-
config
56-
end
57-
end)
46+
|> then(&apply_docker_socket_volume_binding(&1, docker_host))
5847
|> Container.with_auto_remove(true)
5948

6049
with {:ok, _} <- Api.pull_image(ryuk_config.image, conn),
50+
{:ok, docker_hostname} <- get_docker_hostname(docker_host_url, conn),
6151
{:ok, ryuk_container_id} <- Api.create_container(ryuk_config, conn),
6252
:ok <- Api.start_container(ryuk_container_id, conn),
6353
{:ok, container} <- Api.get_container(ryuk_container_id, conn),
64-
{:ok, socket} <- create_ryuk_socket(container),
54+
{:ok, socket} <- create_ryuk_socket(container, docker_hostname),
6555
:ok <- register_ryuk_filter(session_id, socket),
66-
{:ok, docker_hostname} <- get_docker_hostname(docker_host_url, conn),
6756
{:ok, properties} <- PropertiesParser.read_property_file() do
6857
Logger.info("Testcontainers initialized")
6958

@@ -191,13 +180,13 @@ defmodule Testcontainers do
191180
GenServer.call(name, call, @timeout)
192181
end
193182

194-
defp create_ryuk_socket(container, reattempt_count \\ 0)
183+
defp create_ryuk_socket(container, docker_hostname, reattempt_count \\ 0)
195184

196-
defp create_ryuk_socket(%Container{} = container, reattempt_count)
185+
defp create_ryuk_socket(%Container{} = container, docker_hostname, reattempt_count)
197186
when reattempt_count < 3 do
198187
host_port = Container.mapped_port(container, 8080)
199188

200-
case :gen_tcp.connect(~c"localhost", host_port, [
189+
case :gen_tcp.connect(~c"#{docker_hostname}", host_port, [
201190
:binary,
202191
active: false,
203192
packet: :line,
@@ -207,17 +196,17 @@ defmodule Testcontainers do
207196
{:ok, connected}
208197

209198
{:error, :econnrefused} ->
210-
Logger.debug("Connection refused. Retrying... Attempt #{reattempt_count + 1}/3")
199+
Logger.info("Connection refused. Retrying... Attempt #{reattempt_count + 1}/3")
211200
:timer.sleep(5000)
212-
create_ryuk_socket(container, reattempt_count + 1)
201+
create_ryuk_socket(container, docker_hostname, reattempt_count + 1)
213202

214203
{:error, error} ->
215204
{:error, error}
216205
end
217206
end
218207

219-
defp create_ryuk_socket(%Container{} = _container, _reattempt_count) do
220-
Logger.debug("Ryuk host refused to connect")
208+
defp create_ryuk_socket(%Container{} = _container, _docker_hostname, _reattempt_count) do
209+
Logger.info("Ryuk host refused to connect")
221210
{:error, :econnrefused}
222211
end
223212

@@ -291,4 +280,40 @@ defmodule Testcontainers do
291280
error
292281
end)
293282
end
283+
284+
defp apply_docker_socket_volume_binding(config, docker_host) do
285+
case {os_type(), URI.parse(docker_host)} do
286+
{os, uri} -> handle_docker_socket_binding(config, os, uri)
287+
end
288+
end
289+
290+
@dialyzer {:nowarn_function, handle_docker_socket_binding: 3}
291+
defp handle_docker_socket_binding(config, :linux, %URI{scheme: "unix", path: docker_socket_path}) do
292+
Container.with_bind_mount(
293+
config,
294+
docker_socket_path,
295+
"/var/run/docker.sock",
296+
"rw"
297+
)
298+
end
299+
300+
defp handle_docker_socket_binding(config, :macos, %URI{scheme: "unix", path: docker_socket_path}) do
301+
Container.with_bind_mount(
302+
config,
303+
docker_socket_path,
304+
"/var/run/docker.sock",
305+
"rw"
306+
)
307+
end
308+
309+
defp handle_docker_socket_binding(config, :windows, _) do
310+
Container.with_bind_mount(
311+
config,
312+
"//var/run/docker.sock",
313+
"/var/run/docker.sock",
314+
"rw"
315+
)
316+
end
317+
318+
defp handle_docker_socket_binding(config, _, _), do: config
294319
end

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
55
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
66
"cowlib": {:hex, :cowlib, "2.6.0", "8aa629f81a0fc189f261dc98a42243fa842625feea3c7ec56c48f4ccdb55490f", [:rebar3], [], "hexpm", "45a1a08e05e4c66f2af665295955e337d52c2d33b1f1cf24d353cadeddf34992"},
7-
"crc32cer": {:hex, :crc32cer, "0.1.10", "fb87abbf34b72f180f8c3a908cd1826c6cb9a59787d156a29e05de9e98be385e", [:rebar3], [], "hexpm", "5b1f47efd0a1b4b7411f1f35e14d3c8c6da6e6a2a725ec8f2cf1ab13703e5f38"},
7+
"crc32cer": {:hex, :crc32cer, "0.1.11", "b550da6d615feb72a882d15d020f8f7dee72dfb2cb1bcdf3b1ee8dc2afd68cfc", [:rebar3], [], "hexpm", "a39b8f0b1990ac1bf06c3a247fc6a178b740cdfc33c3b53688dc7dd6b1855942"},
88
"credentials_obfuscation": {:hex, :credentials_obfuscation, "3.4.0", "34e18b126b3aefd6e8143776fbe1ceceea6792307c99ac5ee8687911f048cfd7", [:rebar3], [], "hexpm", "738ace0ed5545d2710d3f7383906fc6f6b582d019036e5269c4dbd85dbced566"},
99
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
1010
"decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},

0 commit comments

Comments
 (0)