Packages
testcontainers
2.3.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.PortWaitStrategy do
@moduledoc """
Considers the container as ready when it successfully accepts connections on the specified port.
"""
require Logger
@retry_delay 200
defstruct [:ip, :port, :timeout, retry_delay: @retry_delay]
# Public interface
@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
# Private functions and implementations
defimpl Testcontainers.WaitStrategy do
@impl true
def wait_until_container_is_ready(wait_strategy, container, _conn) do
host = Testcontainers.get_host(container)
host_port = Testcontainers.get_port(container, wait_strategy.port)
case {host, host_port} do
{_, nil} -> {:error, :port_not_mapped, wait_strategy}
_ -> perform_port_check(%{wait_strategy | ip: host}, host_port)
end
end
defp perform_port_check(wait_strategy, host_port) do
started_at = current_time_millis()
case wait_for_open_port(wait_strategy, host_port, started_at) do
:port_is_open ->
:ok
{:error, reason} ->
{:error, reason, wait_strategy}
end
end
defp wait_for_open_port(wait_strategy, host_port, start_time) do
if reached_timeout?(wait_strategy.timeout, start_time) do
{:error, strategy_timed_out(wait_strategy.timeout, start_time)}
else
check_port_status(wait_strategy, host_port, start_time)
end
end
defp check_port_status(wait_strategy, host_port, start_time) do
if port_open?(wait_strategy.ip, host_port) do
:port_is_open
else
log_retry_message(wait_strategy, host_port)
:timer.sleep(wait_strategy.retry_delay)
wait_for_open_port(wait_strategy, host_port, start_time)
end
end
defp port_open?(ip, port, timeout \\ 1000) do
case :gen_tcp.connect(to_charlist(ip), port, [:binary, active: false], timeout) do
{:ok, socket} ->
:gen_tcp.close(socket)
true
{:error, _} ->
false
end
end
defp current_time_millis, do: System.monotonic_time(:millisecond)
defp reached_timeout?(timeout, start_time), do: current_time_millis() - start_time > timeout
defp strategy_timed_out(timeout, start_time) do
{:port_wait_strategy, :timeout, timeout, elapsed_time: current_time_millis() - start_time}
end
defp log_retry_message(wait_strategy, host_port) do
Logger.debug(
"Port #{wait_strategy.port} (host port #{host_port}) not open on IP #{wait_strategy.ip}, retrying in #{wait_strategy.retry_delay}ms."
)
end
end
end