From fe572d64d7819a0ecb566d9f0f0b7c67d8e23e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Mon, 24 Jan 2022 16:53:04 +0100 Subject: [PATCH] Minimal Web handler and initial HAProxy wiring --- lib/ha_handler/application.ex | 3 +- lib/ha_handler/ha_proxy.ex | 62 +++++++++++++++++++++++++++++++++++ lib/ha_handler/web.ex | 22 +++++++++++++ mix.exs | 6 ++-- mix.lock | 15 +++++++++ 5 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 lib/ha_handler/ha_proxy.ex create mode 100644 lib/ha_handler/web.ex create mode 100644 mix.lock diff --git a/lib/ha_handler/application.ex b/lib/ha_handler/application.ex index 38b42cc..dd4d493 100644 --- a/lib/ha_handler/application.ex +++ b/lib/ha_handler/application.ex @@ -8,8 +8,7 @@ defmodule HAHandler.Application do @impl true def start(_type, _args) do children = [ - # Starts a worker by calling: HAHandler.Worker.start_link(arg) - # {HAHandler.Worker, arg} + {Plug.Cowboy, scheme: :http, plug: HAHandler.Web, options: [port: 4000]} ] # See https://hexdocs.pm/elixir/Supervisor.html diff --git a/lib/ha_handler/ha_proxy.ex b/lib/ha_handler/ha_proxy.ex new file mode 100644 index 0000000..15df271 --- /dev/null +++ b/lib/ha_handler/ha_proxy.ex @@ -0,0 +1,62 @@ +defmodule HAHandler.HAProxy do + alias :procket, as: Socket + + @haproxy_socket "/run/haproxy.sock" + + def open_socket() do + pad = 8 * (Socket.unix_path_max() - byte_size(@haproxy_socket)) + + sockaddr = + Socket.sockaddr_common(1, byte_size(@haproxy_socket)) <> @haproxy_socket <> <<0::size(pad)>> + + family = 1 + + {:ok, socket} = Socket.socket(family, 1, 0) + + case Socket.connect(socket, sockaddr) do + :ok -> + {stdin, stdout} = {socket, socket} + port = Port.open({:fd, stdin, stdout}, [{:line, 10_000}, :binary]) + Process.link(port) + + {:ok, port} + + {:error, err} -> + {:error, err} + end + end + + defp read_from_socket(socket, acc \\ "") do + receive do + {_port, {:data, {:noeol, data}}} -> + read_from_socket(socket, acc <> data) + {_port, {:data, {:eol, data}}} -> + Poison.decode(acc <> data) + _ -> + {:error, :unexpected} + after 5000 -> + {:error, :timeout} + end + end + + def get_stats() do + case open_socket() do + {:ok, socket} -> + send socket, {self(), {:command, "show stat json\n"}} + case read_from_socket(socket) do + {:ok, raw} -> + for entry <- raw do + attrs = entry + |> Enum.filter(fn m -> get_in(m, ["field", "name"]) == "pxname" end) + |> Enum.at(0) + + %{type: attrs |> Map.get("objType"), name: get_in(attrs, ["value", "value"])} + end + + {:error, err} -> + {:error, err} + end + {:error, err} -> {:error, err} + end + end +end diff --git a/lib/ha_handler/web.ex b/lib/ha_handler/web.ex new file mode 100644 index 0000000..88f3f56 --- /dev/null +++ b/lib/ha_handler/web.ex @@ -0,0 +1,22 @@ +defmodule HAHandler.Web do + import Plug.Conn + + alias HAHandler.HAProxy + + def init(opts) do + opts + end + + def call(conn, _opts) do + {:ok, hostname} = :inet.gethostname() + + stats = HAProxy.get_stats() + reply = "OK #{hostname}" + <> "\nPROXY: " + <> inspect(stats) + + conn + |> put_resp_content_type("text/plain") + |> send_resp(200, reply) + end +end diff --git a/mix.exs b/mix.exs index fefdee1..09fba6e 100644 --- a/mix.exs +++ b/mix.exs @@ -22,8 +22,10 @@ defmodule HAHandler.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - # {:dep_from_hexpm, "~> 0.3.0"}, - # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + {:plug, "~> 1.12"}, + {:plug_cowboy, "~> 2.5"}, + {:procket, "~> 0.9"}, + {:poison, "~> 5.0"} ] end end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..387ec64 --- /dev/null +++ b/mix.lock @@ -0,0 +1,15 @@ +%{ + "afunix": {:git, "https://github.com/tonyrog/afunix.git", "d7baab77d741d49bce08de2aeb28c5f192ab13d8", []}, + "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, + "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, + "mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"}, + "plug": {:hex, :plug, "1.12.1", "645678c800601d8d9f27ad1aebba1fdb9ce5b2623ddb961a074da0b96c35187d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d57e799a777bc20494b784966dc5fbda91eb4a09f571f76545b72a634ce0d30b"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, + "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, + "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"}, + "procket": {:hex, :procket, "0.9.5", "ce91c00cb3f4d627fb3827f46ce68e6a969ea265df1afb349c300a07be4bb16c", [:rebar3], [], "hexpm", "1288bba5eb6f08ea5df3dda9e051bdac8f4b1e50adcca5cf4d726e825ad81bed"}, + "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, + "sweet_xml": {:hex, :sweet_xml, "0.7.2", "4729f997286811fabdd8288f8474e0840a76573051062f066c4b597e76f14f9f", [:mix], [], "hexpm", "6894e68a120f454534d99045ea3325f7740ea71260bc315f82e29731d570a6e8"}, + "telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"}, +}