Skip to main content

lib/container/minio_container.ex

defmodule TestcontainerEx.MinioContainer do
  @moduledoc """
  Provides functionality for creating and managing Minio container configurations.
  """

  alias TestcontainerEx.Container.Builder
  alias TestcontainerEx.Container.Config
  alias TestcontainerEx.LogWaitStrategy
  alias TestcontainerEx.MinioContainer

  use TestcontainerEx.ContainerConfig

  @default_image "minio/minio"
  @default_tag "RELEASE.2023-11-11T08-14-41Z"
  @default_image_with_tag "#{@default_image}:#{@default_tag}"
  @default_username "minioadmin"
  @default_password "minioadmin"
  @default_s3_port 9000
  @default_ui_port 9001
  @default_wait_timeout 60_000

  @type t :: %__MODULE__{}

  @enforce_keys [:image, :username, :password, :wait_timeout]
  defstruct [
    :image,
    :username,
    :password,
    :wait_timeout,
    :name,
    check_image: @default_image,
    reuse: false
  ]

  def new,
    do: %__MODULE__{
      image: @default_image_with_tag,
      username: @default_username,
      password: @default_password,
      wait_timeout: @default_wait_timeout
    }

  @doc """
  Sets the container name.
  """
  @spec with_name(t(), String.t()) :: t()
  def with_name(%__MODULE__{} = config, name) when is_binary(name) do
    %__MODULE__{config | name: name}
  end

  def get_username, do: @default_username
  def get_password, do: @default_password
  def default_ui_port, do: @default_ui_port
  def default_s3_port, do: @default_s3_port

  def port(%Config{} = container), do: TestcontainerEx.get_port(container, @default_s3_port)

  def connection_url(%Config{} = container) do
    "http://#{TestcontainerEx.get_host(container)}:#{port(container)}"
  end

  def connection_opts(%Config{} = container) do
    [
      port: MinioContainer.port(container),
      scheme: "http://",
      host: TestcontainerEx.get_host(container),
      access_key_id: container.environment[:MINIO_ROOT_USER],
      secret_access_key: container.environment[:MINIO_ROOT_PASSWORD]
    ]
  end

  defimpl Builder do
    @spec build(MinioContainer.t()) :: Config.t()
    @impl true
    def build(%MinioContainer{} = config) do
      Config.new(config.image)
      |> Config.with_exposed_ports([
        MinioContainer.default_s3_port(),
        MinioContainer.default_ui_port()
      ])
      |> Config.with_environment(:MINIO_ROOT_USER, config.username)
      |> Config.with_environment(:MINIO_ROOT_PASSWORD, config.password)
      |> Config.with_reuse(config.reuse)
      |> then(fn cfg ->
        if config.name, do: Config.with_name(cfg, config.name), else: cfg
      end)
      |> Config.with_cmd([
        "server",
        "--console-address",
        ":#{MinioContainer.default_ui_port()}",
        "/data"
      ])
      |> Config.with_waiting_strategy(
        LogWaitStrategy.new(~r/.*Status:         1 Online, 0 Offline..*/, config.wait_timeout)
      )
    end

    @impl true
    def after_start(_config, _container, _conn), do: :ok
  end
end