# Copyright(c) 2015-2023 ACCESS CO., LTD. All rights reserved.
use Croma
defmodule AntikytheraCore.StartupManager do
@moduledoc """
Manages progress of startup procedure of antikythera.
Most of initialization steps are done within `AntikytheraCore.start/2`.
However, the following step is not done in `AntikytheraCore.start/2` and delayed:
- Installing gears:
Starting a gear requires that the antikythera instance (as an OTP application) has started;
this step is delegated to `AntikytheraCore.VersionSynchronizer`.
This `GenServer` waits for the above procedures to complete and then changes the cowboy routing rules
so that the current node can receive web requests from its upstream load balancer.
"""
use GenServer
alias Antikythera.GearName
alias AntikytheraCore.GearManager
alias AntikytheraCore.Handler.CowboyRouting
require AntikytheraCore.Logger, as: L
@typep step :: :all_gears_installed
@typep state :: %{step => boolean}
def start_link([]) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
@impl true
def init(:ok) do
{:ok, %{all_gears_installed: false}}
end
@impl true
def handle_call(:initialized?, _from, state) do
{:reply, all_initialization_steps_finished?(state), state}
end
@impl true
def handle_cast({:update_routing, gear_names}, state) do
CowboyRouting.update_routing(gear_names, all_initialization_steps_finished?(state))
{:noreply, state}
end
def handle_cast(:all_gears_installed, state) do
handle_completion_notice(:all_gears_installed, state)
end
defunp handle_completion_notice(completed_step :: v[atom], old_state :: state) ::
{:noreply, state} do
L.info("received #{completed_step}")
new_state = Map.put(old_state, completed_step, true)
if !all_initialization_steps_finished?(old_state) and
all_initialization_steps_finished?(new_state) do
CowboyRouting.update_routing(GearManager.running_gear_names(), true)
end
{:noreply, new_state}
end
defunp all_initialization_steps_finished?(state :: state) :: boolean do
Enum.all?(Map.values(state))
end
#
# Public API
#
defun initialized?() :: boolean do
GenServer.call(__MODULE__, :initialized?)
end
defun update_routing(gear_names :: [GearName.t()]) :: :ok do
GenServer.cast(__MODULE__, {:update_routing, gear_names})
end
defun all_gears_installed() :: :ok do
GenServer.cast(__MODULE__, :all_gears_installed)
end
end