From 635491d48a6f6b5abade50dcf01266c4cb6ba761 Mon Sep 17 00:00:00 2001 From: Alex Koutmos Date: Thu, 19 Sep 2024 03:47:47 -0400 Subject: [PATCH] Making plug completely optional --- lib/prom_ex/config.ex | 3 +- lib/prom_ex/metrics_server/plug.ex | 131 +++++++++++++++-------------- 2 files changed, 70 insertions(+), 64 deletions(-) diff --git a/lib/prom_ex/config.ex b/lib/prom_ex/config.ex index 80dee05..dc1f23f 100644 --- a/lib/prom_ex/config.ex +++ b/lib/prom_ex/config.ex @@ -199,7 +199,8 @@ defmodule PromEx.Config do * `:metrics_server` - This key contains the configuration information needed to run a standalone HTTP server powered by Cowboy. This server provides a lightweight solution to serving up PromEx - metrics. Its configuration options are: + metrics. In order to use this standalone metrics server plug you need to have `:plug` and `:plug_cowboy` + as dependencies in your project. Its configuration options are: * `:port` - The port that the Cowboy HTTP server should run on. diff --git a/lib/prom_ex/metrics_server/plug.ex b/lib/prom_ex/metrics_server/plug.ex index 831bbef..75680c9 100644 --- a/lib/prom_ex/metrics_server/plug.ex +++ b/lib/prom_ex/metrics_server/plug.ex @@ -1,81 +1,86 @@ -defmodule PromEx.MetricsServer.Plug do - @moduledoc """ - This plug is used to serve metrics when PromEx is run in a - standalone server configuration. This plug supports the - following options: +if Code.ensure_loaded?(Plug) do + defmodule PromEx.MetricsServer.Plug do + @moduledoc """ + This plug is used to serve metrics when PromEx is run in a + standalone server configuration. This plug supports the + following options: - * `:path` - The route that should expose the metrics (default is "/metrics") - * `:prom_ex_module` - The PromEx module that this plug will expose - """ + * `:path` - The route that should expose the metrics (default is "/metrics") + * `:prom_ex_module` - The PromEx module that this plug will expose - @behaviour Plug + In order to use this standalone metrics server plug you need to have `:plug` and `:plug_cowboy` + as dependencies in your project. + """ - require Logger + @behaviour Plug - import Plug.Conn + require Logger - alias Plug.Conn + import Plug.Conn - @impl true - def init(opts) do - Map.pop(opts, :auth_strategy, :none) - end + alias Plug.Conn + + @impl true + def init(opts) do + Map.pop(opts, :auth_strategy, :none) + end - @impl true - def call(%Conn{request_path: path} = conn, {:none, %{path: path, prom_ex_module: prom_ex_module}}) do - case PromEx.get_metrics(prom_ex_module) do - :prom_ex_down -> - Logger.warning("Attempted to fetch metrics from #{prom_ex_module}, but the module has not been initialized") + @impl true + def call(%Conn{request_path: path} = conn, {:none, %{path: path, prom_ex_module: prom_ex_module}}) do + case PromEx.get_metrics(prom_ex_module) do + :prom_ex_down -> + Logger.warning("Attempted to fetch metrics from #{prom_ex_module}, but the module has not been initialized") - conn - |> put_resp_content_type("text/plain") - |> send_resp(503, "Service Unavailable") + conn + |> put_resp_content_type("text/plain") + |> send_resp(503, "Service Unavailable") - metrics -> - PromEx.ETSCronFlusher.defer_ets_flush(prom_ex_module.__ets_cron_flusher_name__()) + metrics -> + PromEx.ETSCronFlusher.defer_ets_flush(prom_ex_module.__ets_cron_flusher_name__()) - conn - |> put_resp_content_type("text/plain") - |> send_resp(200, metrics) + conn + |> put_resp_content_type("text/plain") + |> send_resp(200, metrics) + end end - end - def call( - %Conn{request_path: path} = conn, - {:bearer, %{auth_token: auth_token, path: path} = plug_opts} - ) do - with ["Bearer " <> req_auth_token] <- get_req_header(conn, "authorization"), - true <- req_auth_token == auth_token do - call(conn, {:none, plug_opts}) - else - _ -> - conn - |> put_resp_content_type("text/plain") - |> send_resp(401, "Unauthorized") + def call( + %Conn{request_path: path} = conn, + {:bearer, %{auth_token: auth_token, path: path} = plug_opts} + ) do + with ["Bearer " <> req_auth_token] <- get_req_header(conn, "authorization"), + true <- req_auth_token == auth_token do + call(conn, {:none, plug_opts}) + else + _ -> + conn + |> put_resp_content_type("text/plain") + |> send_resp(401, "Unauthorized") + end end - end - def call( - %Conn{request_path: path} = conn, - {:basic, %{auth_user: auth_user, auth_password: auth_password, path: path} = plug_opts} - ) do - with ["Basic " <> req_auth_user_pass] <- get_req_header(conn, "authorization"), - {:ok, user_and_pass} <- Base.decode64(req_auth_user_pass), - [req_user, req_pass] <- String.split(user_and_pass, ":", parts: 2), - true <- req_user == auth_user, - true <- req_pass == auth_password do - call(conn, {:none, plug_opts}) - else - _ -> - conn - |> put_resp_content_type("text/plain") - |> send_resp(401, "Unauthorized") + def call( + %Conn{request_path: path} = conn, + {:basic, %{auth_user: auth_user, auth_password: auth_password, path: path} = plug_opts} + ) do + with ["Basic " <> req_auth_user_pass] <- get_req_header(conn, "authorization"), + {:ok, user_and_pass} <- Base.decode64(req_auth_user_pass), + [req_user, req_pass] <- String.split(user_and_pass, ":", parts: 2), + true <- req_user == auth_user, + true <- req_pass == auth_password do + call(conn, {:none, plug_opts}) + else + _ -> + conn + |> put_resp_content_type("text/plain") + |> send_resp(401, "Unauthorized") + end end - end - def call(conn, _opts) do - conn - |> put_resp_content_type("text/plain") - |> send_resp(404, "Not Found") + def call(conn, _opts) do + conn + |> put_resp_content_type("text/plain") + |> send_resp(404, "Not Found") + end end end