Skip to main content

lib/container/ceph_container.ex

# SPDX-License-Identifier: MIT
defmodule TestcontainerEx.CephContainer do
  @moduledoc """
  Provides functionality for creating and managing Ceph container configurations.
  """

  alias TestcontainerEx.CephContainer
  alias TestcontainerEx.Container.Builder
  alias TestcontainerEx.Container.Config
  alias TestcontainerEx.LogWaitStrategy

  use TestcontainerEx.ContainerConfig

  @default_image "quay.io/ceph/demo"
  @default_tag "latest-quincy"
  @default_image_with_tag "#{@default_image}:#{@default_tag}"
  @default_bucket "test"
  @default_access_key "test"
  @default_secret_key :crypto.strong_rand_bytes(16) |> Base.encode16(case: :lower)
  @default_port 8080
  @default_wait_timeout 300_000

  @type t :: %__MODULE__{}

  @enforce_keys [:image, :access_key, :secret_key, :bucket, :port, :wait_timeout]
  defstruct [
    :image,
    :access_key,
    :secret_key,
    :bucket,
    :port,
    :wait_timeout,
    :name,
    check_image: @default_image,
    reuse: false
  ]

  def new,
    do: %__MODULE__{
      image: @default_image_with_tag,
      wait_timeout: @default_wait_timeout,
      port: @default_port,
      access_key: @default_access_key,
      secret_key: @default_secret_key,
      bucket: @default_bucket
    }

  def with_image(%__MODULE__{} = config, image) when is_binary(image) do
    %{config | image: image}
  end

  def with_access_key(%__MODULE__{} = config, access_key) when is_binary(access_key) do
    %{config | access_key: access_key}
  end

  def with_secret_key(%__MODULE__{} = config, secret_key) when is_binary(secret_key) do
    %{config | secret_key: secret_key}
  end

  def with_bucket(%__MODULE__{} = config, bucket) when is_binary(bucket) do
    %{config | bucket: bucket}
  end

  def with_port(%__MODULE__{} = config, port) when is_integer(port) do
    %{config | port: port}
  end

  def with_wait_timeout(%__MODULE__{} = config, wait_timeout) when is_integer(wait_timeout) do
    %{config | wait_timeout: wait_timeout}
  end

  @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 default_image, do: @default_image

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

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

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

  defimpl Builder do
    @spec build(CephContainer.t()) :: Config.t()
    @impl true
    def build(%CephContainer{} = config) do
      Config.new(config.image)
      |> Config.with_exposed_port(config.port)
      |> Config.with_environment(:CEPH_DEMO_UID, "demo")
      |> Config.with_environment(:CEPH_DEMO_BUCKET, config.bucket)
      |> Config.with_environment(:CEPH_DEMO_ACCESS_KEY, config.access_key)
      |> Config.with_environment(:CEPH_DEMO_SECRET_KEY, config.secret_key)
      |> Config.with_environment(:CEPH_PUBLIC_NETWORK, "0.0.0.0/0")
      |> Config.with_environment(:MON_IP, "127.0.0.1")
      |> Config.with_environment(:RGW_NAME, "localhost")
      |> Config.with_waiting_strategy(
        LogWaitStrategy.new(
          ~r/.*Bucket 's3:\/\/#{config.bucket}\/' created.*/,
          config.wait_timeout,
          5000
        )
      )
      |> Config.with_check_image(config.check_image)
      |> Config.with_reuse(config.reuse)
      |> then(fn cfg ->
        if config.name, do: Config.with_name(cfg, config.name), else: cfg
      end)
      |> Config.valid_image!()
    end

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