lib/openagents/forge/rolling_node_probe.ex

main at 58e6347eeb72 · 1 KB

defmodule OpenAgents.Forge.RollingNodeProbe do
  @moduledoc """
  Returns the bounded node-local facts required by rolling replacement.

  The infrastructure provider calls this module over Erlang distribution. The
  response contains identity and readiness state only. It never includes a
  database address, credential, request, or application content.
  """

  @doc "Return node health and Ra quorum for an expected fleet size."
  def status(expected_fleet_size)
      when is_integer(expected_fleet_size) and expected_fleet_size > 0 do
    status(expected_fleet_size, nil, nil)
  end

  @doc "Return node health against an operator-approved rolling image identity."
  def status(expected_fleet_size, expected_sha, expected_image_digest)
      when is_integer(expected_fleet_size) and expected_fleet_size > 0 do
    report = OpenAgents.Cluster.local_report()
    ra_members = OpenAgents.Cluster.Ra.members()

    candidate_image? =
      report["revision"] == expected_sha and report["image_digest"] == expected_image_digest

    boot_converged? = report["boot_converged"] == true or candidate_image?

    candidate_ready? =
      candidate_image? and
        report["deployment_ready"] == true and
        report["admission_ready"] == true

    database_ready? = database_ready?()

    %{
      member: true,
      ready: (report["ready"] == true or candidate_ready?) and database_ready?,
      boot_converged: boot_converged?,
      database_ready: database_ready?,
      sha: report["revision"],
      image_digest: report["image_digest"],
      ra_quorum: length(ra_members) * 2 > expected_fleet_size
    }
  end

  defp database_ready? do
    match?({:ok, _result}, OpenAgents.Repo.query("SELECT 1"))
  rescue
    _error -> false
  end
end