diff --git a/config/config.exs b/config/config.exs index 0df80bc..768a042 100644 --- a/config/config.exs +++ b/config/config.exs @@ -3,5 +3,5 @@ import Config config :ha_handler, http_port: 4000, acme_challenge_path: "acme-challenge", - haproxy_socket: System.get_env("HAPROXY_SOCKET") || "/var/run/haproxy.sock" - + haproxy_socket: System.get_env("HAPROXY_SOCKET") || "/var/run/haproxy.sock", + pgsql_instances: [] diff --git a/lib/ha_handler.ex b/lib/ha_handler.ex index 8ab75e7..2700fc0 100644 --- a/lib/ha_handler.ex +++ b/lib/ha_handler.ex @@ -11,6 +11,8 @@ defmodule HAHandler do def http_port, do: Application.get_env(@otp_app, :http_port) def haproxy_socket, do: Application.get_env(@otp_app, :haproxy_socket) + def pgsql_instances, do: Application.get_env(@otp_app, :pgsql_instances, []) + def acme_challenge_path, do: Application.get_env(@otp_app, :acme_challenge_path) def static_path(), do: Application.app_dir(@otp_app, "priv/static/") diff --git a/lib/ha_handler/application.ex b/lib/ha_handler/application.ex index bb02cbf..d928238 100644 --- a/lib/ha_handler/application.ex +++ b/lib/ha_handler/application.ex @@ -5,12 +5,13 @@ defmodule HAHandler.Application do use Application - import HAHandler + alias HAHandler @impl true def start(_type, _args) do children = [ - {Plug.Cowboy, scheme: :http, plug: HAHandler.Web.Router, options: [port: http_port()]} + {Plug.Cowboy, scheme: :http, plug: HAHandler.Web.Router, options: [port: HAHandler.http_port()]}, + {HAHandler.PGSQL.Supervisor, HAHandler.pgsql_instances()} ] # See https://hexdocs.pm/elixir/Supervisor.html diff --git a/lib/ha_handler/pgsql.ex b/lib/ha_handler/pgsql.ex new file mode 100644 index 0000000..3107d83 --- /dev/null +++ b/lib/ha_handler/pgsql.ex @@ -0,0 +1,10 @@ +defmodule HAHandler.PGSQL do + @supervisor HAHandler.PGSQL.Supervisor + + def status() do + watchers = Supervisor.which_children(@supervisor) + for {_id, pid, _type, _modules} <- watchers do + GenServer.call(pid, :status) + end + end +end diff --git a/lib/ha_handler/pgsql/supervisor.ex b/lib/ha_handler/pgsql/supervisor.ex new file mode 100644 index 0000000..8c826ac --- /dev/null +++ b/lib/ha_handler/pgsql/supervisor.ex @@ -0,0 +1,17 @@ +defmodule HAHandler.PGSQL.Supervisor do + use Supervisor + + alias HAHandler.PGSQL.Watcher, as: PGSQLWatcher + + def start_link(opts) do + Supervisor.start_link(__MODULE__, opts, name: __MODULE__) + end + + @impl true + def init(instances) do + children = Enum.map(instances, fn conf -> {PGSQLWatcher, conf} end) + + opts = [ strategy: :one_for_one ] + Supervisor.init(children, opts) + end +end diff --git a/lib/ha_handler/pgsql/watcher.ex b/lib/ha_handler/pgsql/watcher.ex new file mode 100644 index 0000000..e3ce328 --- /dev/null +++ b/lib/ha_handler/pgsql/watcher.ex @@ -0,0 +1,42 @@ +defmodule HAHandler.PGSQL.Watcher do + use GenServer + require Logger + + def start_link(opts) do + GenServer.start_link(__MODULE__, opts, name: __MODULE__) + end + + @impl true + def init(opts) do + # Starts a Postgrex child but does not means the connection was + # successful. + # TODO: set dbconnections backoff and connect hooks + # See https://github.com/elixir-ecto/db_connection/blob/master/lib/db_connection.ex#L343 + {:ok, pid} = Postgrex.start_link(opts) + + state = %{ + backend: pid, + hostname: Keyword.get(opts, :hostname) + } + + {:ok, state} + end + + @impl true + def handle_call(:status, _from, %{backend: backend, hostname: hostname} = state) do + result = case Postgrex.query(backend, "SELECT version();", []) do + {:ok, %Postgrex.Result{rows: [[raw_version_string]]}} -> + version = case Regex.run(~r/^PostgreSQL (\d+\.\d+)/, raw_version_string) do + [_, version_number] -> version_number + _ -> "unknown" + end + %{hostname: hostname, version: version, status: "up"} + {:error, %DBConnection.ConnectionError{message: _msg, reason: err}} -> + %{hostname: hostname, version: "unknown", status: err} + _ -> + %{hostname: hostname, version: "unknown", status: "Unhandled error"} + end + + {:reply, result, state} + end +end diff --git a/lib/ha_handler/web/controller.ex b/lib/ha_handler/web/controller.ex index 01b7d4f..448af9c 100644 --- a/lib/ha_handler/web/controller.ex +++ b/lib/ha_handler/web/controller.ex @@ -1,7 +1,7 @@ defmodule HAHandler.Web.Controller do import Plug.Conn - alias HAHandler.HAProxy + alias HAHandler.{HAProxy, PGSQL} @templates_dir "lib/ha_handler/web/templates" @@ -22,6 +22,7 @@ defmodule HAHandler.Web.Controller do assigns = [ haproxy_stats: stats, + pgsql_status: PGSQL.status(), hostname: hostname, otp_app: HAHandler.otp_app(), version: HAHandler.version(), diff --git a/lib/ha_handler/web/templates/index.html.eex b/lib/ha_handler/web/templates/index.html.eex index 3e62aa7..629d172 100644 --- a/lib/ha_handler/web/templates/index.html.eex +++ b/lib/ha_handler/web/templates/index.html.eex @@ -98,6 +98,29 @@ <% end %> + +
Hostname | +Version | +Status | +
---|---|---|
<%= entry[:hostname] %> | +<%= entry[:version] %> | +<%= entry[:status] %> | +