defmodule Elstat.Error.Service do defexception message: "default message" end defmodule Elstat.ServiceWorker do require Logger def start_link(service) do Logger.info "spawning worker for #{inspect service.id}" worker_pid = spawn(fn -> worker_func(service) end) {:ok, worker_pid} end def worker_func(service) do adapter = service.adapter result = adapter.check(service.adapter_opts) Logger.info "result from #{inspect adapter} #{inspect service.id}: #{inspect result}" man_pid = :global.whereis_name Elstat.Manager send man_pid, {:service_info, {service.id, result}} Process.sleep(service.poll * 1000) worker_func(service) end end defmodule Elstat.Manager do @moduledoc """ Elstat's service manager. """ use GenServer require Logger def start_link(opts) do GenServer.start_link(__MODULE__, :ok, [name: {:global, Elstat.Manager}] ++ opts) end def build_services() do services = Application.fetch_env!(:elstat, :services) workers = services |> Map.keys |> Enum.map(fn service_id -> service = Map.put(services[service_id], :id, service_id) # each service worker will enter an infinite loop # polling the service (via an adapter) and giving # information back to the manager, so it can # process and show those via its API. %{ id: service_id, start: {Elstat.ServiceWorker, :start_link, [service]} } end) # spawn the managere alongside service workers [ Elstat.Manager | workers] end def get_current_state() do man_pid = :global.whereis_name Elstat.Manager GenServer.call(man_pid, {:get_state}) end # server callbacks def init(:ok) do services = Application.fetch_env!(:elstat, :services) Sqlitex.with_db('elstat.db', fn(db) -> Enum.each(services, fn service -> end) end) {:ok, %{ services: services, serv_state: %{}, }} end def handle_call({:get_state}, _from, state) do reply = state.serv_state |> Map.keys |> Enum.map(fn key -> data = state.serv_state[key] desc = state.services[key].description result = case data do {:map, data_map} -> {key, Map.put(data_map, :description, desc)} {:bool, data_bool} -> {key, %{ status: data_bool, description: desc, }} end end) |> Map.new {:reply, reply, state} end def handle_info({:service_info, {sid, sdata}}, state) do case sdata do {:ok, actual_data} -> new_serv_state = Map.put(state.serv_state, sid, actual_data) {:noreply, %{state | serv_state: new_serv_state}} {:error, err} -> Logger.warn "error on #{inspect sid}: #{inspect err}" end end end