Packages
testcontainers
1.2.7
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/command_wait_strategy.ex
# SPDX-License-Identifier: MIT
# Original by: Marco Dallagiacoma @ 2023 in https://github.com/dallagi/excontainers
# Modified by: Jarl André Hübenthal @ 2023
defmodule Testcontainers.WaitStrategy.CommandWaitStrategy do
@moduledoc """
Considers container as ready as soon as a command runs successfully inside the container.
"""
@retry_delay 200
defstruct [:command, :timeout, retry_delay: @retry_delay]
@doc """
Creates a new CommandWaitStrategy to wait until the given command executes successfully inside the container.
"""
def new(command, timeout \\ 5000, retry_delay \\ @retry_delay),
do: %__MODULE__{command: command, timeout: timeout, retry_delay: retry_delay}
defimpl Testcontainers.WaitStrategy do
alias Testcontainers.Logger
alias Testcontainers.WaitStrategy.CommandWaitStrategy
@impl true
def wait_until_container_is_ready(%CommandWaitStrategy{} = wait_strategy, id_or_name) do
# Capture the start time of the process
started_at = current_time_millis()
# Call the recursive function
recursive_wait(wait_strategy, id_or_name, started_at)
end
# Recursive function with breaking conditions
defp recursive_wait(%CommandWaitStrategy{} = wait_strategy, id_or_name, started_at) do
case exec_and_wait(
id_or_name,
wait_strategy.command,
wait_strategy.timeout,
wait_strategy.retry_delay
) do
{:ok, 0} ->
:ok
{:ok, other_exit_code} ->
if out_of_time(started_at, wait_strategy.timeout) do
{:error, strategy_timed_out(wait_strategy.timeout, started_at), wait_strategy}
else
delay = max(0, wait_strategy.retry_delay)
Logger.log(
"Command execution in container #{id_or_name} failed with exit_code #{other_exit_code}, retrying in #{delay}ms."
)
:timer.sleep(delay)
recursive_wait(wait_strategy, id_or_name, started_at)
end
{:error, reason} ->
{:error, reason, wait_strategy}
end
end
def exec_and_wait(container_id, command, timeout, retry_delay) do
{:ok, exec_id} = exec(container_id, command)
started_at = current_time_millis()
case wait_for_exec_result(exec_id, timeout, started_at, retry_delay) do
{:ok, exec_info} -> {:ok, exec_info.exit_code}
{:error, error} -> {:error, error}
end
end
def exec(container_id, command) do
with {:ok, exec_id} <- Testcontainers.exec_create(container_id, command),
:ok <- Testcontainers.exec_start(exec_id) do
{:ok, exec_id}
end
end
defp wait_for_exec_result(
exec_id,
timeout_ms,
started_at,
retry_delay
) do
case Testcontainers.exec_inspect(exec_id) do
{:ok, %{running: true}} ->
do_wait_unless_timed_out(exec_id, timeout_ms, started_at, retry_delay)
{:ok, finished_exec_status} ->
{:ok, finished_exec_status}
end
end
defp do_wait_unless_timed_out(exec_id, timeout, started_at, retry_delay) do
if out_of_time(started_at, timeout) do
{:error, strategy_timed_out(timeout, started_at)}
else
delay = max(0, retry_delay)
:timer.sleep(delay)
wait_for_exec_result(exec_id, timeout, started_at, retry_delay)
end
end
defp current_time_millis, do: System.monotonic_time(:millisecond)
defp out_of_time(started_at, timeout_ms), do: current_time_millis() - started_at > timeout_ms
defp strategy_timed_out(timeout, started_at)
when is_number(timeout) and is_number(started_at),
do:
{:command_wait_strategy, :timeout, timeout,
elapsed_time: current_time_millis() - started_at}
end
end