Skip to main content

lib/container/cassandra_container.ex

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

  alias TestcontainerEx.CassandraContainer
  alias TestcontainerEx.CommandWaitStrategy
  alias TestcontainerEx.Container.Builder
  alias TestcontainerEx.Container.Config

  use TestcontainerEx.ContainerConfig

  @default_image "cassandra"
  @default_tag "3.11.2"
  @default_image_with_tag "#{@default_image}:#{@default_tag}"
  @default_username "cassandra"
  @default_password "cassandra"
  @default_port 9042
  @default_wait_timeout 60_000

  @type t :: %__MODULE__{}

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

  def new,
    do: %__MODULE__{
      image: @default_image_with_tag,
      wait_timeout: @default_wait_timeout
    }

  def with_image(%__MODULE__{} = config, image) when is_binary(image) do
    %{config | image: image}
  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 default_port, do: @default_port
  def get_username, do: @default_username
  def get_password, do: @default_password

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

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

  defimpl Builder do
    @impl true
    @spec build(CassandraContainer.t()) :: Config.t()
    def build(%CassandraContainer{} = config) do
      Config.new(config.image)
      |> Config.with_exposed_port(CassandraContainer.default_port())
      |> Config.with_environment(:CASSANDRA_SNITCH, "GossipingPropertyFileSnitch")
      |> Config.with_environment(
        :JVM_OPTS,
        "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0"
      )
      |> Config.with_environment(:HEAP_NEWSIZE, "128M")
      |> Config.with_environment(:MAX_HEAP_SIZE, "1024M")
      |> Config.with_environment(:CASSANDRA_ENDPOINT_SNITCH, "GossipingPropertyFileSnitch")
      |> Config.with_environment(:CASSANDRA_DC, "datacenter1")
      |> Config.with_waiting_strategy(
        CommandWaitStrategy.new(
          ["cqlsh", "-e", "describe keyspaces"],
          config.wait_timeout
        )
      )
      |> 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