lib/openagents/forge/rolling_provider/gcp/deployer.ex

main at 58e6347eeb72 · 2 KB

defmodule OpenAgents.Forge.RollingProvider.Gcp.Deployer do
  @moduledoc """
  Starts the minimal private BEAM node used for Google Cloud replacement calls.

  The deployer container starts this module with the release's clean boot file.
  It does not start the OpenAgents application, join Ra, open an HTTP listener,
  or connect to PostgreSQL. It starts only the dependencies needed by `Req` and
  then waits for bounded `erpc` calls from the three-node staging fleet.
  """

  @sha_pattern ~r/\A[0-9a-f]{40}\z/
  @deployer_node :"openagents-deployer@openagents-deployer.staging.internal"

  @doc false
  def start do
    expected_revision = System.fetch_env!("OPENAGENTS_CONTROLLER_SHA")

    unless Regex.match?(@sha_pattern, expected_revision) and
             expected_revision == OpenAgents.BuildInfo.revision() do
      raise "deployer image revision does not match its assigned Git SHA"
    end

    :ok = configure_provider!()
    {:ok, _applications} = Application.ensure_all_started(:req)
    Process.sleep(:infinity)
  end

  @doc false
  def configure_provider!(environment \\ System.get_env()) when is_map(environment) do
    config = [
      project_id: fetch!(environment, "OPENAGENTS_GCP_ROLLING_PROJECT_ID"),
      production_project_id: fetch!(environment, "OPENAGENTS_PRODUCTION_PROJECT_ID"),
      zone: fetch!(environment, "OPENAGENTS_GCP_ROLLING_ZONE"),
      instances: decode_instances!(environment),
      image_repository: fetch!(environment, "OPENAGENTS_GCP_IMAGE_REPOSITORY"),
      deployer_node: @deployer_node,
      rpc_timeout_ms: 5_000,
      compute_timeout_ms: 300_000
    ]

    case OpenAgents.Forge.RollingProvider.Gcp.validate_config(config) do
      :ok ->
        Application.put_env(:openagents, OpenAgents.Forge.RollingProvider.Gcp, config)

      {:error, _reason} ->
        raise ArgumentError, "staging rolling-provider configuration is invalid"
    end
  end

  defp decode_instances!(environment) do
    environment
    |> fetch!("OPENAGENTS_GCP_ROLLING_INSTANCES_JSON")
    |> Jason.decode()
    |> case do
      {:ok, instances} when is_map(instances) -> instances
      _invalid -> raise ArgumentError, "staging rolling-provider configuration is invalid"
    end
  end

  defp fetch!(environment, name) do
    case Map.fetch(environment, name) do
      {:ok, value} when is_binary(value) and value != "" -> value
      _missing -> raise ArgumentError, "staging rolling-provider configuration is invalid"
    end
  end
end