Packages
testcontainers
1.0.0-beta.1
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/connection.ex
# SPDX-License-Identifier: MIT
defmodule Testcontainers.Connection do
use GenServer
alias Testcontainers.Utils
alias Testcontainers.Connection.DockerHostStrategyEvaluator
alias Testcontainers.Connection.DockerHostStrategy.DockerSocketPath
alias Testcontainers.Connection.DockerHostStrategy.DockerHostFromProperties
alias Testcontainers.Connection.DockerHostStrategy.DockerHostFromEnv
alias Testcontainers.Connection.DockerHostStrategy.DockerHostFromProperties
alias Testcontainers.Container
alias Testcontainers.Docker.Api
alias DockerEngineAPI.Connection
@api_version "v1.41"
@timeout 300_000
@doc """
Eagerly starts this genserver unlinked and waits until it is registered
"""
def start_eager(options \\ []) do
case GenServer.whereis(__MODULE__) do
nil ->
start_unlinked(options)
pid when is_pid(pid) ->
{:ok, pid}
end
end
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def pull_image(image) when is_binary(image) do
GenServer.call(__MODULE__, {:pull_image, image}, 300_000)
end
def create_container(%Container{} = container) do
GenServer.call(__MODULE__, {:create_container, container}, 300_000)
end
def start_container(container_id) when is_binary(container_id) do
GenServer.call(__MODULE__, {:start_container, container_id}, 300_000)
end
def stop_container(container_id) when is_binary(container_id) do
GenServer.call(__MODULE__, {:stop_container, container_id}, 300_000)
end
def get_container(container_id) when is_binary(container_id) do
GenServer.call(__MODULE__, {:get_container, container_id}, 300_000)
end
def stdout_logs(container_id) when is_binary(container_id) do
GenServer.call(__MODULE__, {:stdout_logs, container_id}, 300_000)
end
def exec_create(container_id, command) when is_binary(container_id) and is_list(command) do
GenServer.call(__MODULE__, {:exec_create, command, container_id}, 300_000)
end
def exec_start(exec_id) when is_binary(exec_id) do
GenServer.call(__MODULE__, {:exec_start, exec_id}, 300_000)
end
def exec_inspect(exec_id) when is_binary(exec_id) do
GenServer.call(__MODULE__, {:exec_inspect, exec_id}, 300_000)
end
@impl true
def init(options) do
{:ok, get_connection(options)}
end
@impl true
def handle_call({:pull_image, image}, _from, connection) do
{:reply, Api.pull_image(image, connection), connection}
end
@impl true
def handle_call({:get_container, container_id}, _from, connection) do
{:reply, Api.get_container(container_id, connection), connection}
end
@impl true
def handle_call({:start_container, container_id}, _from, connection) do
{:reply, Api.start_container(container_id, connection), connection}
end
@impl true
def handle_call({:create_container, %Container{} = container}, _from, connection) do
{:reply, Api.create_container(container, connection), connection}
end
@impl true
def handle_call({:stop_container, container_id}, _from, connection) do
{:reply, Api.stop_container(container_id, connection), connection}
end
@impl true
def handle_call({:stdout_logs, container_id}, _from, connection) do
{:reply, Api.stdout_logs(container_id, connection), connection}
end
@impl true
def handle_call({:exec_create, command, container_id}, _from, connection) do
{:reply, Api.create_exec(container_id, command, connection), connection}
end
@impl true
def handle_call({:exec_start, exec_id}, _from, connection) do
{:reply, Api.start_exec(exec_id, connection), connection}
end
@impl true
def handle_call({:exec_inspect, exec_id}, _from, connection) do
{:reply, Api.inspect_exec(exec_id, connection), connection}
end
def get_connection(options \\ []) do
docker_host_url = docker_base_url()
Utils.log("Using docker host url: #{docker_host_url}")
options = Keyword.merge(options, base_url: docker_host_url, recv_timeout: @timeout)
Connection.new(options)
end
defp docker_base_url do
strategies = [
%DockerHostFromProperties{key: "tc.host"},
%DockerHostFromEnv{},
%DockerSocketPath{socket_paths: ["/var/run/docker.sock"]},
%DockerHostFromProperties{key: "docker.host"},
%DockerSocketPath{}
]
case DockerHostStrategyEvaluator.run_strategies(strategies, []) do
{:ok, "unix://" <> path} ->
"http+unix://#{:uri_string.quote(path)}/#{@api_version}"
{:ok, docker_host} ->
construct_url_from_docker_host(docker_host)
:error ->
exit("Failed to find docker host")
end
end
defp construct_url_from_docker_host(docker_host) do
uri = URI.parse(docker_host)
case uri do
%URI{scheme: "tcp"} ->
URI.to_string(%{uri | scheme: "http", path: "/#{@api_version}"})
%URI{scheme: _, authority: _} = uri ->
URI.to_string(%{uri | path: "/#{@api_version}"})
end
end
defp start_unlinked(options) do
spawn(fn -> GenServer.start(__MODULE__, options, name: __MODULE__) end)
wait_for_start(__MODULE__, 10_000)
end
defp wait_for_start(name, timeout) do
wait_for_start(name, timeout, :timer.seconds(1))
end
defp wait_for_start(_name, 0, _interval), do: {:error, :timeout}
defp wait_for_start(name, remaining_time, interval) do
case GenServer.whereis(name) do
nil ->
Process.sleep(interval)
wait_for_start(name, remaining_time - interval, interval)
pid when is_pid(pid) ->
{:ok, pid}
end
end
end