Current section

Files

Jump to
testcontainers lib connection.ex
Raw

lib/connection.ex

# SPDX-License-Identifier: MIT
defmodule Testcontainers.Connection do
use GenServer
require Logger
alias Testcontainers.Container
alias Testcontainers.Docker.Api
alias DockerEngineAPI.Connection
@default_host "unix:///var/run/docker.sock"
@api_version "v1.41"
@timeout 300_000
@doc """
Eagerly starts this genserver unlinked and waits until it is registered
"""
def start_eager(opts \\ []) do
case GenServer.whereis(__MODULE__) do
nil ->
start_unlinked(opts)
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(_) do
{:ok, get_connection()}
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
options = Keyword.merge(options, base_url: docker_base_url(), recv_timeout: @timeout)
Connection.new(options)
end
defp docker_base_url do
case System.get_env("DOCKER_HOST", @default_host) do
"unix://" <> host -> "http+unix://" <> :uri_string.quote(host) <> "/" <> @api_version
"tcp://" <> host -> "http://" <> :uri_string.quote(host) <> "/" <> @api_version
end
end
defp start_unlinked(opts) do
spawn(fn -> GenServer.start(__MODULE__, opts, 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