Set editorconfig, format whole codebase
This commit is contained in:
parent
e62aafd172
commit
ebcfabdbd2
11 changed files with 380 additions and 329 deletions
12
.editorconfig
Normal file
12
.editorconfig
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*.ex]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.eex]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
|
@ -6,8 +6,8 @@ defmodule HAHandler do
|
||||||
# Mix is not available in releases, and these things are static
|
# Mix is not available in releases, and these things are static
|
||||||
# anyway (@variables are evaluated at compile time).
|
# anyway (@variables are evaluated at compile time).
|
||||||
@otp_app Mix.Project.config()[:app]
|
@otp_app Mix.Project.config()[:app]
|
||||||
@version Mix.Project.config[:version]
|
@version Mix.Project.config()[:version]
|
||||||
@env Mix.env
|
@env Mix.env()
|
||||||
|
|
||||||
def http_port, do: Application.get_env(@otp_app, :http_port)
|
def http_port, do: Application.get_env(@otp_app, :http_port)
|
||||||
def haproxy_socket, do: Application.get_env(@otp_app, :haproxy_socket)
|
def haproxy_socket, do: Application.get_env(@otp_app, :haproxy_socket)
|
||||||
|
@ -26,6 +26,7 @@ defmodule HAHandler do
|
||||||
from: acme_challenge_path()
|
from: acme_challenge_path()
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def assets_static_config() do
|
def assets_static_config() do
|
||||||
[
|
[
|
||||||
at: "/static",
|
at: "/static",
|
||||||
|
|
|
@ -10,7 +10,8 @@ defmodule HAHandler.Application do
|
||||||
@impl true
|
@impl true
|
||||||
def start(_type, _args) do
|
def start(_type, _args) do
|
||||||
children = [
|
children = [
|
||||||
{Plug.Cowboy, scheme: :http, plug: HAHandler.Web.Router, options: [port: HAHandler.http_port()]},
|
{Plug.Cowboy,
|
||||||
|
scheme: :http, plug: HAHandler.Web.Router, options: [port: HAHandler.http_port()]},
|
||||||
{HAHandler.PGSQL.Supervisor, HAHandler.pgsql_instances()},
|
{HAHandler.PGSQL.Supervisor, HAHandler.pgsql_instances()},
|
||||||
{HAHandler.Control, []}
|
{HAHandler.Control, []}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,82 +1,104 @@
|
||||||
defmodule HAHandler.Control do
|
defmodule HAHandler.Control do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This module handles the decision-logic and actions to be
|
This module handles the decision-logic and actions to be
|
||||||
taken regarding the current state of the infrastructure.
|
taken regarding the current state of the infrastructure.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@haproxy_pgsql_backend "pgsql"
|
@haproxy_pgsql_backend "pgsql"
|
||||||
|
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias HAHandler.{PGSQL, HAProxy}
|
alias HAHandler.{PGSQL, HAProxy}
|
||||||
|
|
||||||
# How much do we wait (ms) between each check/decision-making round?
|
# How much do we wait (ms) between each check/decision-making round?
|
||||||
@refresh 15_000
|
@refresh 15_000
|
||||||
|
|
||||||
def start_link(opts) do
|
def start_link(opts) do
|
||||||
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init(_opts) do
|
def init(_opts) do
|
||||||
state = []
|
state = []
|
||||||
|
|
||||||
# Let's skip the initial startup round so that other components are all up
|
# Let's skip the initial startup round so that other components are all up
|
||||||
# and running.
|
# and running.
|
||||||
Process.send_after self(), :sync, @refresh
|
Process.send_after(self(), :sync, @refresh)
|
||||||
|
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info(:sync, state) do
|
def handle_info(:sync, state) do
|
||||||
Logger.debug("Executing control logic.")
|
Logger.debug("Executing control logic.")
|
||||||
|
|
||||||
# Fetch PGSQL state, make sure HAProxy routes to the master
|
# Fetch PGSQL state, make sure HAProxy routes to the master
|
||||||
# process.
|
# process.
|
||||||
pgsql_state = PGSQL.get_instances()
|
pgsql_state =
|
||||||
|> Enum.map(fn {hostname, pid} = instance->
|
PGSQL.get_instances()
|
||||||
haproxy_server = HAHandler.pgsql_instances()
|
|> Enum.map(fn {hostname, pid} = instance ->
|
||||||
|> Enum.filter(fn opts -> Keyword.get(opts, :hostname) == hostname end)
|
haproxy_server =
|
||||||
|> Enum.at(0)
|
HAHandler.pgsql_instances()
|
||||||
|> Keyword.get(:haproxy_server)
|
|> Enum.filter(fn opts -> Keyword.get(opts, :hostname) == hostname end)
|
||||||
|
|> Enum.at(0)
|
||||||
|
|> Keyword.get(:haproxy_server)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
haproxy_server: haproxy_server,
|
haproxy_server: haproxy_server,
|
||||||
pgsql_watcher_pid: pid,
|
pgsql_watcher_pid: pid,
|
||||||
pgsql_operation_mode: PGSQL.get_operation_mode(instance)
|
pgsql_operation_mode: PGSQL.get_operation_mode(instance)
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
haproxy_state = HAProxy.get_stats()
|
|
||||||
|> Map.get("Server", [])
|
|
||||||
|> Enum.filter(fn mapping -> mapping["pxname"] == @haproxy_pgsql_backend end)
|
|
||||||
|> Enum.map(fn mapping -> %{mapping["svname"] => mapping["status"]} end)
|
|
||||||
|> Enum.reduce(&Map.merge/2)
|
|
||||||
|
|
||||||
for pgsql_instance <- pgsql_state do
|
haproxy_state =
|
||||||
haproxy_state = Map.get(haproxy_state, pgsql_instance.haproxy_server)
|
HAProxy.get_stats()
|
||||||
|
|> Map.get("Server", [])
|
||||||
|
|> Enum.filter(fn mapping -> mapping["pxname"] == @haproxy_pgsql_backend end)
|
||||||
|
|> Enum.map(fn mapping -> %{mapping["svname"] => mapping["status"]} end)
|
||||||
|
|> Enum.reduce(&Map.merge/2)
|
||||||
|
|
||||||
case {pgsql_instance.pgsql_operation_mode, haproxy_state} do
|
for pgsql_instance <- pgsql_state do
|
||||||
{:primary, "UP"} ->
|
haproxy_state = Map.get(haproxy_state, pgsql_instance.haproxy_server)
|
||||||
:noop
|
|
||||||
{:primary, "MAINT"} ->
|
|
||||||
Logger.info("Enabling routing PGSQL to (now) primary #{pgsql_instance.haproxy_server}.")
|
|
||||||
HAProxy.set_server(@haproxy_pgsql_backend, pgsql_instance.haproxy_server, "state", "ready")
|
|
||||||
{:secondary, "UP"} ->
|
|
||||||
Logger.info("Disabling routing PGSQL to (now) secondary #{pgsql_instance.haproxy_server}.")
|
|
||||||
HAProxy.set_server(@haproxy_pgsql_backend, pgsql_instance.haproxy_server, "state", "maint")
|
|
||||||
{:secondary, "MAINT"} ->
|
|
||||||
:noop
|
|
||||||
unknown ->
|
|
||||||
Logger.warning("Unhandled PGSQL/HAProxy state: #{inspect(unknown)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Schedule next round.
|
case {pgsql_instance.pgsql_operation_mode, haproxy_state} do
|
||||||
Process.send_after self(), :sync, @refresh
|
{:primary, "UP"} ->
|
||||||
|
:noop
|
||||||
|
|
||||||
{:noreply, state}
|
{:primary, "MAINT"} ->
|
||||||
end
|
Logger.info("Enabling routing PGSQL to (now) primary #{pgsql_instance.haproxy_server}.")
|
||||||
|
|
||||||
|
HAProxy.set_server(
|
||||||
|
@haproxy_pgsql_backend,
|
||||||
|
pgsql_instance.haproxy_server,
|
||||||
|
"state",
|
||||||
|
"ready"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:secondary, "UP"} ->
|
||||||
|
Logger.info(
|
||||||
|
"Disabling routing PGSQL to (now) secondary #{pgsql_instance.haproxy_server}."
|
||||||
|
)
|
||||||
|
|
||||||
|
HAProxy.set_server(
|
||||||
|
@haproxy_pgsql_backend,
|
||||||
|
pgsql_instance.haproxy_server,
|
||||||
|
"state",
|
||||||
|
"maint"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:secondary, "MAINT"} ->
|
||||||
|
:noop
|
||||||
|
|
||||||
|
unknown ->
|
||||||
|
Logger.warning("Unhandled PGSQL/HAProxy state: #{inspect(unknown)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Schedule next round.
|
||||||
|
Process.send_after(self(), :sync, @refresh)
|
||||||
|
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,28 +33,31 @@ defmodule HAHandler.HAProxy do
|
||||||
# run out - which will block all operations.
|
# run out - which will block all operations.
|
||||||
close_socket(fd)
|
close_socket(fd)
|
||||||
data
|
data
|
||||||
|
|
||||||
{:error, err} ->
|
{:error, err} ->
|
||||||
{:error, err}
|
{:error, err}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp extract_stats(data) do
|
defp extract_stats(data) do
|
||||||
extracted = for entry <- data do
|
extracted =
|
||||||
for mapping <- entry do
|
for entry <- data do
|
||||||
case mapping do
|
for mapping <- entry do
|
||||||
%{
|
case mapping do
|
||||||
"id" => id,
|
%{
|
||||||
"proxyId" => proxy_id,
|
"id" => id,
|
||||||
"objType" => type,
|
"proxyId" => proxy_id,
|
||||||
"field" => %{"name" => name},
|
"objType" => type,
|
||||||
"value" => %{"value" => value},
|
"field" => %{"name" => name},
|
||||||
} ->
|
"value" => %{"value" => value}
|
||||||
%{:id => id, :proxy_id => proxy_id, :type => type, :field => name, :value => value}
|
} ->
|
||||||
_ ->
|
%{:id => id, :proxy_id => proxy_id, :type => type, :field => name, :value => value}
|
||||||
nil
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
extracted
|
extracted
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|
@ -62,18 +65,14 @@ defmodule HAHandler.HAProxy do
|
||||||
fn mapping -> {mapping.type, mapping.id, mapping.proxy_id} end,
|
fn mapping -> {mapping.type, mapping.id, mapping.proxy_id} end,
|
||||||
fn mapping -> %{mapping.field => mapping.value} end
|
fn mapping -> %{mapping.field => mapping.value} end
|
||||||
)
|
)
|
||||||
|> Enum.map(
|
|> Enum.map(fn {{type, id, proxy_id}, grouped_mappings} ->
|
||||||
fn {{type, id, proxy_id}, grouped_mappings} ->
|
grouped_mappings
|
||||||
grouped_mappings
|
|> Enum.reduce(fn l, r -> Map.merge(l, r) end)
|
||||||
|> Enum.reduce(fn l, r -> Map.merge(l,r) end)
|
|> Map.put("type", type)
|
||||||
|> Map.put("type", type)
|
|> Map.put("id", id)
|
||||||
|> Map.put("id", id)
|
|> Map.put("proxy_id", proxy_id)
|
||||||
|> Map.put("proxy_id", proxy_id)
|
end)
|
||||||
end
|
|> Enum.group_by(fn entry -> Map.get(entry, "type") end)
|
||||||
)
|
|
||||||
|> Enum.group_by(
|
|
||||||
fn entry -> Map.get(entry, "type") end
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -81,7 +80,8 @@ defmodule HAHandler.HAProxy do
|
||||||
a list of Maps.
|
a list of Maps.
|
||||||
"""
|
"""
|
||||||
def get_stats(opts \\ [])
|
def get_stats(opts \\ [])
|
||||||
def get_stats([hide_error: true]) do
|
|
||||||
|
def get_stats(hide_error: true) do
|
||||||
case get_stats() do
|
case get_stats() do
|
||||||
{:error, _err} ->
|
{:error, _err} ->
|
||||||
%{
|
%{
|
||||||
|
@ -89,9 +89,12 @@ defmodule HAHandler.HAProxy do
|
||||||
"Backend" => %{},
|
"Backend" => %{},
|
||||||
"Server" => %{}
|
"Server" => %{}
|
||||||
}
|
}
|
||||||
stats -> stats
|
|
||||||
|
stats ->
|
||||||
|
stats
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_stats(_opts) do
|
def get_stats(_opts) do
|
||||||
case execute("show stat json") do
|
case execute("show stat json") do
|
||||||
{:ok, raw} ->
|
{:ok, raw} ->
|
||||||
|
@ -117,8 +120,10 @@ defmodule HAHandler.HAProxy do
|
||||||
case execute("set server #{backend}/#{server} #{key} #{value}") do
|
case execute("set server #{backend}/#{server} #{key} #{value}") do
|
||||||
{:ok, ""} ->
|
{:ok, ""} ->
|
||||||
:ok
|
:ok
|
||||||
|
|
||||||
{:ok, err} ->
|
{:ok, err} ->
|
||||||
{:error, err}
|
{:error, err}
|
||||||
|
|
||||||
{:error, err} ->
|
{:error, err} ->
|
||||||
{:error, err}
|
{:error, err}
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,45 +1,52 @@
|
||||||
defmodule HAHandler.PGSQL do
|
defmodule HAHandler.PGSQL do
|
||||||
@supervisor HAHandler.PGSQL.Supervisor
|
@supervisor HAHandler.PGSQL.Supervisor
|
||||||
@version_query "SELECT version();"
|
@version_query "SELECT version();"
|
||||||
@is_in_recovery_query "SELECT pg_is_in_recovery();"
|
@is_in_recovery_query "SELECT pg_is_in_recovery();"
|
||||||
|
|
||||||
def get_instances() do
|
def get_instances() do
|
||||||
watchers = Supervisor.which_children(@supervisor)
|
watchers = Supervisor.which_children(@supervisor)
|
||||||
for {hostname, pid, _type, _modules} <- watchers do
|
|
||||||
{hostname, pid}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_version({hostname, pid}) do
|
for {hostname, pid, _type, _modules} <- watchers do
|
||||||
case GenServer.call(pid, {:execute, @version_query, []}) do
|
{hostname, pid}
|
||||||
{:ok, %Postgrex.Result{rows: [[raw_version_string]]}} ->
|
end
|
||||||
version = case Regex.run(~r/^PostgreSQL (\d+\.\d+)/, raw_version_string) do
|
end
|
||||||
[_, 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: :unknown}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_operation_mode({_hostname, pid}) do
|
def get_version({hostname, pid}) do
|
||||||
case GenServer.call(pid, {:execute, @is_in_recovery_query, []}) do
|
case GenServer.call(pid, {:execute, @version_query, []}) do
|
||||||
{:ok, %Postgrex.Result{rows: [[false]]}} ->
|
{:ok, %Postgrex.Result{rows: [[raw_version_string]]}} ->
|
||||||
:primary
|
version =
|
||||||
{:ok, %Postgrex.Result{rows: [[true]]}} ->
|
case Regex.run(~r/^PostgreSQL (\d+\.\d+)/, raw_version_string) do
|
||||||
:secondary
|
[_, version_number] -> version_number
|
||||||
_ ->
|
_ -> "unknown"
|
||||||
:unknown
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_stats() do
|
%{hostname: hostname, version: version, status: "up"}
|
||||||
get_instances()
|
|
||||||
|> Enum.map(fn instance ->
|
{:error, %DBConnection.ConnectionError{message: _msg, reason: err}} ->
|
||||||
get_version(instance) |> Map.put(:mode, get_operation_mode(instance))
|
%{hostname: hostname, version: "unknown", status: err}
|
||||||
end)
|
|
||||||
end
|
_ ->
|
||||||
|
%{hostname: hostname, version: "unknown", status: :unknown}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_operation_mode({_hostname, pid}) do
|
||||||
|
case GenServer.call(pid, {:execute, @is_in_recovery_query, []}) do
|
||||||
|
{:ok, %Postgrex.Result{rows: [[false]]}} ->
|
||||||
|
:primary
|
||||||
|
|
||||||
|
{:ok, %Postgrex.Result{rows: [[true]]}} ->
|
||||||
|
:secondary
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
:unknown
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_stats() do
|
||||||
|
get_instances()
|
||||||
|
|> Enum.map(fn instance ->
|
||||||
|
get_version(instance) |> Map.put(:mode, get_operation_mode(instance))
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
defmodule HAHandler.PGSQL.Supervisor do
|
defmodule HAHandler.PGSQL.Supervisor do
|
||||||
use Supervisor
|
use Supervisor
|
||||||
|
|
||||||
alias HAHandler.PGSQL.Watcher, as: PGSQLWatcher
|
alias HAHandler.PGSQL.Watcher, as: PGSQLWatcher
|
||||||
|
|
||||||
def start_link(opts) do
|
def start_link(opts) do
|
||||||
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
|
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init(instances) do
|
def init(instances) do
|
||||||
children = Enum.map(instances, fn conf ->
|
children =
|
||||||
%{
|
Enum.map(instances, fn conf ->
|
||||||
id: Keyword.get(conf, :hostname),
|
%{
|
||||||
start: {PGSQLWatcher, :start_link, [conf]}
|
id: Keyword.get(conf, :hostname),
|
||||||
}
|
start: {PGSQLWatcher, :start_link, [conf]}
|
||||||
end)
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
opts = [ strategy: :one_for_one ]
|
opts = [strategy: :one_for_one]
|
||||||
Supervisor.init(children, opts)
|
Supervisor.init(children, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
defmodule HAHandler.PGSQL.Watcher do
|
defmodule HAHandler.PGSQL.Watcher do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def start_link(opts) do
|
def start_link(opts) do
|
||||||
GenServer.start_link(__MODULE__, opts)
|
GenServer.start_link(__MODULE__, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init(opts) do
|
def init(opts) do
|
||||||
# Starts a Postgrex child but does not means the connection was
|
# Starts a Postgrex child but does not means the connection was
|
||||||
# successful.
|
# successful.
|
||||||
# TODO: set dbconnections backoff and connect hooks
|
# TODO: set dbconnections backoff and connect hooks
|
||||||
# See https://github.com/elixir-ecto/db_connection/blob/master/lib/db_connection.ex#L343
|
# See https://github.com/elixir-ecto/db_connection/blob/master/lib/db_connection.ex#L343
|
||||||
{:ok, pid} = Postgrex.start_link(opts)
|
{:ok, pid} = Postgrex.start_link(opts)
|
||||||
|
|
||||||
state = %{
|
state = %{
|
||||||
backend: pid,
|
backend: pid,
|
||||||
hostname: Keyword.get(opts, :hostname)
|
hostname: Keyword.get(opts, :hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_call({:execute, query, params}, _from, %{backend: backend} = state) do
|
def handle_call({:execute, query, params}, _from, %{backend: backend} = state) do
|
||||||
{:reply, Postgrex.query(backend, query, params), state}
|
{:reply, Postgrex.query(backend, query, params), state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,35 +1,34 @@
|
||||||
defmodule HAHandler.Web.Controller do
|
defmodule HAHandler.Web.Controller do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
alias HAHandler.{HAProxy, PGSQL}
|
alias HAHandler.{HAProxy, PGSQL}
|
||||||
|
|
||||||
@template_dir "lib/ha_handler/web/templates"
|
@template_dir "lib/ha_handler/web/templates"
|
||||||
@index_template EEx.compile_file(
|
@index_template EEx.compile_file(Path.join(@template_dir, "index.html.eex"))
|
||||||
Path.join(@template_dir, "index.html.eex")
|
|
||||||
)
|
|
||||||
|
|
||||||
defp render(conn, template, assigns) do
|
defp render(conn, template, assigns) do
|
||||||
{body, _binding} = Code.eval_quoted(template, assigns)
|
{body, _binding} = Code.eval_quoted(template, assigns)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("text/html")
|
|> put_resp_content_type("text/html")
|
||||||
|> send_resp(200, body)
|
|> send_resp(200, body)
|
||||||
end
|
end
|
||||||
|
|
||||||
def index(conn) do
|
def index(conn) do
|
||||||
{:ok, hostname} = :net_adm.dns_hostname(:net_adm.localhost)
|
{:ok, hostname} = :net_adm.dns_hostname(:net_adm.localhost())
|
||||||
|
|
||||||
haproxy_stats = HAProxy.get_stats([hide_error: true])
|
haproxy_stats = HAProxy.get_stats(hide_error: true)
|
||||||
pgsql_stats = PGSQL.get_stats()
|
pgsql_stats = PGSQL.get_stats()
|
||||||
|
|
||||||
assigns = [
|
assigns = [
|
||||||
haproxy_stats: haproxy_stats,
|
haproxy_stats: haproxy_stats,
|
||||||
pgsql_status: pgsql_stats,
|
pgsql_status: pgsql_stats,
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
otp_app: HAHandler.otp_app(),
|
otp_app: HAHandler.otp_app(),
|
||||||
version: HAHandler.version(),
|
version: HAHandler.version(),
|
||||||
env: HAHandler.env()
|
env: HAHandler.env()
|
||||||
]
|
]
|
||||||
render(conn, @index_template, assigns)
|
|
||||||
end
|
render(conn, @index_template, assigns)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,35 +1,38 @@
|
||||||
defmodule HAHandler.Web.Router do
|
defmodule HAHandler.Web.Router do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This module dispatch incoming HTTP requests to their
|
This module dispatch incoming HTTP requests to their
|
||||||
related logic. Please refer to [1] for details.
|
related logic. Please refer to [1] for details.
|
||||||
|
|
||||||
[1] https://hexdocs.pm/plug/Plug.Router.html#content
|
[1] https://hexdocs.pm/plug/Plug.Router.html#content
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use Plug.Router
|
use Plug.Router
|
||||||
|
|
||||||
alias HAHandler.Web.Controller
|
alias HAHandler.Web.Controller
|
||||||
|
|
||||||
# Note for plugs: oder is important, as a plug may stop
|
# Note for plugs: oder is important, as a plug may stop
|
||||||
# want to stop the pipeline!
|
# want to stop the pipeline!
|
||||||
|
|
||||||
plug Plug.Logger, log: :debug
|
plug(Plug.Logger, log: :debug)
|
||||||
|
|
||||||
# We use replug to allow for runtime configuration in release (as macros such
|
# We use replug to allow for runtime configuration in release (as macros such
|
||||||
# as the `plug` call ae evaluated are compile-time).
|
# as the `plug` call ae evaluated are compile-time).
|
||||||
plug Replug,
|
plug(Replug,
|
||||||
plug: Plug.Static,
|
plug: Plug.Static,
|
||||||
opts: {HAHandler, :acme_challenges_static_config}
|
opts: {HAHandler, :acme_challenges_static_config}
|
||||||
plug Replug,
|
)
|
||||||
plug: Plug.Static,
|
|
||||||
opts: {HAHandler, :assets_static_config}
|
|
||||||
|
|
||||||
plug :match
|
plug(Replug,
|
||||||
plug :dispatch
|
plug: Plug.Static,
|
||||||
|
opts: {HAHandler, :assets_static_config}
|
||||||
|
)
|
||||||
|
|
||||||
get "/", do: Controller.index(conn)
|
plug(:match)
|
||||||
|
plug(:dispatch)
|
||||||
|
|
||||||
match _ do
|
get("/", do: Controller.index(conn))
|
||||||
send_resp(conn, 404, "Not found")
|
|
||||||
end
|
match _ do
|
||||||
|
send_resp(conn, 404, "Not found")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,124 +1,124 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>[HA] <%= hostname %></title>
|
<title>[HA] <%= hostname %></title>
|
||||||
<link rel="stylesheet" href="/static/app.css">
|
<link rel="stylesheet" href="/static/app.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<div>
|
<div>
|
||||||
<img id="logo" src="/static/logo.svg" />
|
<img id="logo" src="/static/logo.svg" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1>Recycled Cloud HA handler</h1>
|
<h1>Recycled Cloud HA handler</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This service supervises the various components of
|
This service supervises the various components of
|
||||||
the Recycled Cloud's High Availability
|
the Recycled Cloud's High Availability
|
||||||
infrastruture. Documentation and source code can be
|
infrastruture. Documentation and source code can be
|
||||||
found on <a
|
found on <a
|
||||||
href="https://code.recycled.cloud/RecycledCloud/ha-handler">our
|
href="https://code.recycled.cloud/RecycledCloud/ha-handler">our
|
||||||
software forge</a>.
|
software forge</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>Handler</h2>
|
<h2>Handler</h2>
|
||||||
|
|
||||||
<%= otp_app %> <b>v<%= version %></b> (<%= env %>) running on <b><%= hostname %></b>
|
<%= otp_app %> <b>v<%= version %></b> (<%= env %>) running on <b><%= hostname %></b>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>HAProxy</h2>
|
<h2>HAProxy</h2>
|
||||||
|
|
||||||
<h3>Frontends</h3>
|
<h3>Frontends</h3>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Bytes in</th>
|
<th>Bytes in</th>
|
||||||
<th>Bytes out</th>
|
<th>Bytes out</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<%= for entry <- Map.get(haproxy_stats, "Frontend") do %>
|
<%= for entry <- Map.get(haproxy_stats, "Frontend") do %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= entry["pxname"] %></td>
|
<td><%= entry["pxname"] %></td>
|
||||||
<td><%= entry["status"] %></td>
|
<td><%= entry["status"] %></td>
|
||||||
<td><%= entry["bin"] %></td>
|
<td><%= entry["bin"] %></td>
|
||||||
<td><%= entry["bout"] %></td>
|
<td><%= entry["bout"] %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Backends</h3>
|
<h3>Backends</h3>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>algo</th>
|
<th>algo</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<%= for entry <- Map.get(haproxy_stats, "Backend") do %>
|
<%= for entry <- Map.get(haproxy_stats, "Backend") do %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= entry["pxname"] %></td>
|
<td><%= entry["pxname"] %></td>
|
||||||
<td><%= entry["status"] %></td>
|
<td><%= entry["status"] %></td>
|
||||||
<td><%= entry["algo"] %></td>
|
<td><%= entry["algo"] %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Servers</h3>
|
<h3>Servers</h3>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Mode</th>
|
<th>Mode</th>
|
||||||
<th>Address</th>
|
<th>Address</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<%= for entry <- Map.get(haproxy_stats, "Server") do %>
|
<%= for entry <- Map.get(haproxy_stats, "Server") do %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= entry["pxname"] %>/<%= entry["svname"] %></td>
|
<td><%= entry["pxname"] %>/<%= entry["svname"] %></td>
|
||||||
<td><%= entry["status"] %></td>
|
<td><%= entry["status"] %></td>
|
||||||
<td><%= entry["mode"] %></td>
|
<td><%= entry["mode"] %></td>
|
||||||
<td><%= entry["addr"] %></td>
|
<td><%= entry["addr"] %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>PostgreSQL</h2>
|
<h2>PostgreSQL</h2>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Hostname</th>
|
<th>Hostname</th>
|
||||||
<th>Version</th>
|
<th>Version</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Operation</th>
|
<th>Operation</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<%= for entry <- pgsql_status do %>
|
<%= for entry <- pgsql_status do %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= entry[:hostname] %></td>
|
<td><%= entry[:hostname] %></td>
|
||||||
<td><%= entry[:version] %></td>
|
<td><%= entry[:version] %></td>
|
||||||
<td><%= entry[:status] %></td>
|
<td><%= entry[:status] %></td>
|
||||||
<td><%= entry[:mode] %></td>
|
<td><%= entry[:mode] %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue