# SPDX-License-Identifier: MIT defmodule Testcontainers.Container.CephContainer do @moduledoc """ Module for building Ceph container configurations. This module provides functions for creating and manipulating configurations for Ceph containers. It allows the setting of specific parameters like image, access keys, secret keys, and other parameters related to the Ceph container. """ alias Testcontainers.WaitStrategy.LogWaitStrategy alias Testcontainers.Container.CephContainer alias Testcontainers.ContainerBuilder alias Testcontainers.Container @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 UUID.uuid4() @default_port 8080 @default_wait_timeout 300_000 @enforce_keys [:image, :access_key, :secret_key, :bucket, :port, :wait_timeout] defstruct [:image, :access_key, :secret_key, :bucket, :port, :wait_timeout] @doc """ Creates a new `CephContainer` struct with default attributes. """ 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 } @doc """ Sets the `image` of the Ceph container configuration. ## Examples iex> config = CephContainer.new() iex> new_config = CephContainer.with_image(config, "quay.io/ceph/alternative") iex> new_config.image "quay.io/ceph/alternative" """ def with_image(%__MODULE__{} = config, image) when is_binary(image) do %{config | image: image} end @doc """ Sets the `access_key` used for authentication with the Ceph container. ## Examples iex> config = CephContainer.new() iex> new_config = CephContainer.with_access_key(config, "new_access_key") iex> new_config.access_key "new_access_key" """ def with_access_key(%__MODULE__{} = config, access_key) when is_binary(access_key) do %{config | access_key: access_key} end @doc """ Sets the `secret_key` used for authentication with the Ceph container. ## Examples iex> config = CephContainer.new() iex> new_config = CephContainer.with_secret_key(config, "new_secret_key") iex> new_config.secret_key "new_secret_key" """ def with_secret_key(%__MODULE__{} = config, secret_key) when is_binary(secret_key) do %{config | secret_key: secret_key} end @doc """ Sets the `bucket` that is automatically in the Ceph container. ## Examples iex> config = CephContainer.new() iex> new_config = CephContainer.with_bucket(config, "test_bucket") iex> new_config.bucket "test_bucket" """ def with_bucket(%__MODULE__{} = config, bucket) when is_binary(bucket) do %{config | bucket: bucket} end @doc """ Sets the port on which the Ceph container will be exposed. ## Parameters - `config`: The current Ceph container configuration. - `port`: The target port number. ## Examples iex> config = CephContainer.new() iex> new_config = CephContainer.with_port(config, 8081) iex> new_config.port 8081 """ def with_port(%__MODULE__{} = config, port) when is_integer(port) do %{config | port: port} end @doc """ Sets the maximum time (in milliseconds) the system will wait for the Ceph container to be ready before timing out. ## Parameters - `config`: The current Ceph container configuration. - `wait_timeout`: The time to wait in milliseconds. ## Examples iex> config = CephContainer.new() iex> new_config = CephContainer.with_wait_timeout(config, 400_000) iex> new_config.wait_timeout 400_000 """ def with_wait_timeout(%__MODULE__{} = config, wait_timeout) when is_integer(wait_timeout) do %{config | wait_timeout: wait_timeout} end @doc """ Retrieves the default Docker image used for the Ceph container. ## Examples iex> CephContainer.default_image() "quay.io/ceph/demo" """ def default_image, do: @default_image @doc """ Retrieves the port mapped by the Docker host for the Ceph container. ## Parameters - `container`: The active Ceph container instance. ## Examples iex> CephContainer.port(container) 32768 # This value will be different depending on the mapped port. """ def port(%Container{} = container), do: Container.mapped_port(container, @default_port) @doc """ Generates the connection URL for accessing the Ceph service running within the container. This URL is based on the standard localhost IP and the mapped port for the container. ## Parameters - `container`: The active Ceph container instance. ## Examples iex> CephContainer.connection_url(container) "http://localhost:32768" # This value will be different depending on the mapped port. """ def connection_url(%Container{} = container) do "http://localhost:#{port(container)}" end defimpl ContainerBuilder do import Container @doc """ Implementation of the `ContainerBuilder` protocol for `CephContainer`. This implementation provides the logic for building a container configuration specific to Ceph. It ensures the provided image is compatible, sets up necessary environment variables, configures network settings, and applies a waiting strategy to ensure the container is fully operational before it's used. The build process raises an `ArgumentError` if the specified container image is not compatible with the expected Ceph image. ## Examples # Assuming `ContainerBuilder.build/2` is called from somewhere in the application with a `CephContainer` configuration: iex> config = CephContainer.new() iex> built_container = ContainerBuilder.build(config, []) # `built_container` is now a ready-to-use `%Container{}` configured specifically for Ceph. ## Errors - Raises `ArgumentError` if the provided image is not compatible with the default Ceph image. """ @spec build(%CephContainer{}) :: %Container{} @impl true def build(%CephContainer{} = config) do if not String.starts_with?(config.image, CephContainer.default_image()) do raise ArgumentError, message: "Image #{config.image} is not compatible with #{CephContainer.default_image()}" end new(config.image) |> with_exposed_port(config.port) |> with_environment(:CEPH_DEMO_UID, "demo") |> with_environment(:CEPH_DEMO_BUCKET, config.bucket) |> with_environment(:CEPH_DEMO_ACCESS_KEY, config.access_key) |> with_environment(:CEPH_DEMO_SECRET_KEY, config.secret_key) |> with_environment(:CEPH_PUBLIC_NETWORK, "0.0.0.0/0") |> with_environment(:MON_IP, "127.0.0.1") |> with_environment(:RGW_NAME, "localhost") |> with_waiting_strategy( LogWaitStrategy.new( ~r/.*Bucket 's3:\/\/#{config.bucket}\/' created.*/, config.wait_timeout, 5000 ) ) end end end