Packages
testcontainers
1.2.0
2.3.1
2.3.0
2.2.0
2.1.0
2.1.0-rc1
2.0.0
2.0.0-rc3
2.0.0-rc2
2.0.0-rc
1.14.1
1.14.0
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.8
1.11.7
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
1.11.0
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-beta.1
1.0.0-beta
0.9.3
0.9.2
0.9.1
0.9.0
Testcontainers is an Elixir library that supports ExUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
Current section
Files
Jump to
Current section
Files
lib/wait_strategy/port_wait_strategy.ex
# SPDX-License-Identifier: MIT
defmodule Testcontainers.WaitStrategy.PortWaitStrategy do
@moduledoc """
Considers the container as ready when it successfully accepts connections on the specified port.
"""
@retry_delay 200
defstruct [:ip, :port, :timeout, retry_delay: @retry_delay]
@doc """
Creates a new PortWaitStrategy to wait until a specified port is open and accepting connections.
"""
def new(ip, port, timeout \\ 5000, retry_delay \\ @retry_delay),
do: %__MODULE__{ip: ip, port: port, timeout: timeout, retry_delay: retry_delay}
end
defimpl Testcontainers.WaitStrategy, for: Testcontainers.WaitStrategy.PortWaitStrategy do
alias Testcontainers.Connection
alias Testcontainers.Container
alias Testcontainers.Utils
def wait_until_container_is_ready(wait_strategy, id_or_name) do
with {:ok, %Container{} = container} <- Connection.get_container(id_or_name) do
host_port = Container.mapped_port(container, wait_strategy.port)
if host_port == nil do
{:error, {:no_host_port, wait_strategy.port}}
else
start_time = current_time_millis()
case wait_for_port(wait_strategy, host_port, start_time) do
{:ok, :port_is_open} ->
:ok
{:error, reason} ->
{:error, reason, wait_strategy}
end
end
end
end
defp wait_for_port(wait_strategy, host_port, start_time)
when is_integer(host_port) and is_integer(start_time) do
if wait_strategy.timeout + start_time < current_time_millis() do
{:error, strategy_timed_out(wait_strategy.timeout, start_time)}
else
if port_open?(wait_strategy.ip, host_port) do
{:ok, :port_is_open}
else
delay = max(0, wait_strategy.retry_delay)
Utils.log(
"Port #{wait_strategy.port} not open on IP #{wait_strategy.ip}, retrying in #{delay}ms."
)
:timer.sleep(delay)
wait_for_port(wait_strategy, host_port, start_time)
end
end
end
defp port_open?(ip, port, timeout \\ 1000)
when is_binary(ip) and is_integer(port) and is_integer(timeout) do
case :gen_tcp.connect(~c"#{ip}", port, [:binary, active: false], timeout) do
{:ok, socket} ->
:gen_tcp.close(socket)
true
{:error, _reason} ->
false
end
end
defp current_time_millis, do: System.monotonic_time(:millisecond)
defp strategy_timed_out(timeout, started_at) when is_number(timeout) and is_number(started_at),
do: {:port_wait_strategy, :timeout, timeout, elapsed_time: current_time_millis() - started_at}
end