Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace custom admin dashboard with phoenix_live_dashboard #143

Merged
merged 1 commit into from
May 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ that I'd like to improve.

### Admin area

There is a minimal admin page where you can see some stats about current lassos on [/admin](http://localhost:4000/admin). This page is protected by basic auth, the credentials can be configured in `config.exs`. By default the password is read from the environment variable `ADMIN_PASSWORD`.
There's a Phoenix Live Dashboard available on [/admin](http://localhost:4000/admin). This page is protected by basic auth, the credentials can be configured in `config.exs`. By default the password is read from the environment variable `ADMIN_PASSWORD`.

## Building a release

Expand Down
131 changes: 68 additions & 63 deletions lib/lasso.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ defmodule Lasso do
@cache_id Application.get_env(:lasso, Lasso)[:cache_name]
@request_limit Application.get_env(:lasso, Lasso)[:max_requests_per_lasso]

@active_lassos_key :active_lassos
@total_lassos_key :total_lassos

@admin_events Application.get_env(:lasso, Lasso)[:admin_events_topic]

@topic inspect(__MODULE__)

def subscribe(uuid) do
Expand All @@ -21,47 +16,40 @@ defmodule Lasso do
@doc """
Create a new lasso that we need to keep track of
"""
def create(uuid, opts \\ [update_stats: true]) do
result = ConCache.put(@cache_id, uuid, [])
if opts[:update_stats], do: update_stats()
result
end

defp update_stats() do
ConCache.update(@cache_id, @active_lassos_key, fn val ->
case val do
nil -> {:ok, 1}
val -> {:ok, val + 1}
end
end)

ConCache.update(@cache_id, @total_lassos_key, fn val ->
case val do
nil -> {:ok, 1}
val -> {:ok, val + 1}
end
end)

notify_stats()
def create(uuid, _opts \\ [update_stats: true]) do
with_telemetry_events(
fn -> ConCache.put(@cache_id, uuid, []) end,
{:action, %{action: :create}}
)
end

@doc """
Get all the requests for a lasso
"""
def get(uuid) do
case ConCache.get(@cache_id, uuid) do
nil -> {:error, :no_such_key, uuid}
value -> {:ok, value}
end
with_telemetry_events(
fn ->
case ConCache.get(@cache_id, uuid) do
nil -> {:error, :no_such_key, uuid}
value -> {:ok, value}
end
end,
{:action, %{action: :get}}
)
end

@doc """
Append a request to a lasso
"""
def add(uuid, request) do
with :ok <- update(uuid, request) do
notify_subscribers(uuid, {:request, request})
end
with_telemetry_events(
fn ->
with :ok <- update(uuid, request) do
notify_subscribers(uuid, {:request, request})
end
end,
{:request, %{request: request}}
)
end

defp notify_subscribers(uuid, data) do
Expand All @@ -72,18 +60,28 @@ defmodule Lasso do
Delete a lasso
"""
def delete(uuid) do
ConCache.delete(@cache_id, uuid)
notify_subscribers(uuid, :delete)
with_telemetry_events(
fn ->
ConCache.delete(@cache_id, uuid)
notify_subscribers(uuid, :delete)
end,
{:action, %{action: :delete}}
)
end

@doc """
Clear all requests for a lasso
"""
def clear(uuid) do
with {:ok, _} <- get(uuid),
:ok <- create(uuid, update_stats: false) do
notify_subscribers(uuid, :clear)
end
with_telemetry_events(
fn ->
with {:ok, _} <- get(uuid),
:ok <- create(uuid, update_stats: false) do
notify_subscribers(uuid, :clear)
end
end,
{:action, %{action: :clear}}
)
end

@doc """
Expand All @@ -92,29 +90,8 @@ defmodule Lasso do
def cache_callback({:update, _cache_pid, _key, _value}), do: :ok

def cache_callback({:delete, _cache_pid, _key}) do
ConCache.update(@cache_id, @active_lassos_key, fn val ->
case val do
nil -> {:ok, 0}
val -> {:ok, max(0, val - 1)}
end
end)

notify_stats()
end

@doc """
Get statistics about lassos
"""
def stats() do
active_lassos = ConCache.get(@cache_id, @active_lassos_key) || 0
total_lassos = ConCache.get(@cache_id, @total_lassos_key) || 0
{:ok, %{"active_lassos" => active_lassos, "total_lassos" => total_lassos}}
end

defp notify_stats() do
with {:ok, stats} <- stats() do
notify_subscribers(@admin_events, {:stats, stats})
end
emit_stop({:action, %{action: :delete}}, 1)
:ok
end

defp update(uuid, request) do
Expand All @@ -129,4 +106,32 @@ defmodule Lasso do
end
end)
end

defp with_telemetry_events(my_fn, path_meta) do
start_time = emit_start(path_meta)
result = my_fn.()
duration = System.monotonic_time() - start_time
emit_stop(path_meta, duration)
result
end

defp emit_start({path, meta}) do
start_time_mono = System.monotonic_time()

:telemetry.execute(
[:lasso, path, :start],
%{system_time: System.system_time()},
meta
)

start_time_mono
end

defp emit_stop({path, meta}, duration) do
:telemetry.execute(
[:lasso, path, :stop],
%{duration: duration},
meta
)
end
end
7 changes: 1 addition & 6 deletions lib/lasso/application.ex
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
defmodule Lasso.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false

use Application

def start(_type, _args) do
# List all child processes to be supervised
children = [
{ConCache,
[
Expand All @@ -17,10 +14,8 @@ defmodule Lasso.Application do
callback: &Lasso.cache_callback/1
]},
{Phoenix.PubSub, name: Lasso.PubSub},
# Start the endpoint when the application starts
LassoWeb.Telemetry,
LassoWeb.Endpoint
# Starts a worker by calling: Lasso.Worker.start_link(arg)
# {Lasso.Worker, arg},
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
17 changes: 0 additions & 17 deletions lib/lasso_web/controllers/admin_controller.ex

This file was deleted.

11 changes: 10 additions & 1 deletion lib/lasso_web/router.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule LassoWeb.Router do
use LassoWeb, :router
import Phoenix.LiveView.Router
import Phoenix.LiveDashboard.Router

pipeline :browser do
plug :accepts, ["html"]
Expand All @@ -18,18 +19,26 @@ defmodule LassoWeb.Router do
plug :accepts, ["json"]
end

pipeline :admins_only do
plug BasicAuth, use_config: {:basic_auth, :admin_area}
end

scope "/", LassoWeb do
pipe_through :browser

get "/", PageController, :index
get "/admin", AdminController, :index

delete "/admin/lasso/:uuid", LassoViewController, :delete
post "/admin/lasso/", LassoViewController, :new

get "/lasso/:uuid/view", LassoViewController, :show
end

scope "/admin" do
pipe_through [:browser, :admins_only]
live_dashboard "/", metrics: LassoWeb.Telemetry
end

# The `get` here gets flagged by Sobelow as a potential for "CSRF via Action Reuse"
# It does not matter if a user can trigger the post request in this scenario.
scope "/lasso", LassoWeb do
Expand Down
65 changes: 65 additions & 0 deletions lib/lasso_web/telemetry.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
defmodule LassoWeb.Telemetry do
@moduledoc """
Metrics for the admin live dashboard
"""

use Supervisor
import Telemetry.Metrics

def start_link(arg) do
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
end

@impl true
def init(_arg) do
children = [
# Telemetry poller will execute the given period measurements
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
# Add reporters as children of your supervision tree.
# {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
]

Supervisor.init(children, strategy: :one_for_one)
end

def metrics do
[
# Phoenix Metrics
summary("phoenix.endpoint.stop.duration",
unit: {:native, :millisecond}
),
summary("phoenix.router_dispatch.stop.duration",
tags: [:route],
unit: {:native, :millisecond}
),

# Lasso specifics
summary("lasso.action.stop.duration",
tags: [:action],
tag_values: &action_tag_value/1,
unit: {:native, :millisecond}
),
counter("lasso.action.stop.duration", tags: [:action], tag_values: &action_tag_value/1),
counter("lasso.request.stop.duration", tags: [:method], tag_values: &request_method_value/1),

# VM Metrics
summary("vm.memory.total", unit: {:byte, :kilobyte}),
summary("vm.total_run_queue_lengths.total"),
summary("vm.total_run_queue_lengths.cpu"),
summary("vm.total_run_queue_lengths.io")
]
end

defp periodic_measurements do
[]
end

defp action_tag_value(metadata) do
Map.take(metadata, [:action])
end

defp request_method_value(metadata) do
Map.take(metadata.request, [:method])
end
end
4 changes: 4 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ defmodule Lasso.MixProject do
{:elixir_uuid, "~> 1.2"},
{:con_cache, "~> 0.13"},
{:basic_auth, "~> 2.2"},
{:phoenix_live_dashboard, "~> 0.2"},
{:telemetry_poller, "~> 0.4"},
{:telemetry_metrics, "~> 0.4"},
{:telemetry, "~> 0.4"},
{:credo, "~> 1.1", only: [:dev, :test], runtime: false},
{:sobelow, "~> 0.8", only: :dev, runtime: false},
{:floki, "~> 0.26", only: [:test], runtime: false}
Expand Down
5 changes: 4 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
"phoenix": {:hex, :phoenix, "1.5.3", "bfe0404e48ea03dfe17f141eff34e1e058a23f15f109885bbdcf62be303b49ff", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8e16febeb9640d8b33895a691a56481464b82836d338bb3a23125cd7b6157c25"},
"phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.2.4", "3080e8a89bab3ec08d4dd9a6858dfa24af9334464aae78c83e58a2db37c6f983", [:mix], [{:phoenix_html, "~> 2.14.1 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.12.0 or ~> 0.13.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.4.0 or ~> 0.5.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "1c89595ef60f1b76ac07705e73f001823af451491792a4b0d5b2b2a3789b0a00"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.2", "38d94c30df5e2ef11000697a4fbe2b38d0fbf79239d492ff1be87bbc33bc3a84", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "a3dec3d28ddb5476c96a7c8a38ea8437923408bc88da43e5c45d97037b396280"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.12.1", "42f591c781edbf9fab921319076b7ac635d43aa23e6748d2644563326236d7e4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.4.16 or ~> 1.5.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "585321e98df1cd5943e370b9784e950a37ca073744eb534660c9048967c52ab6"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.13.0", "dec006b3da4ab164283d5bebe960724eb4d19cd0ed553e05fb99b260233e200f", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.4.17 or ~> 1.5.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "bd6f13b666fa9bfeca88b013db20414c693d5a5e6d19b1fc2602c282d626ed8e"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
"plug": {:hex, :plug, "1.10.1", "c56a6d9da7042d581159bcbaef873ba9d87f15dce85420b0d287bca19f40f9bd", [:mix], [{:mime, "~> 1.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", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "b5cd52259817eb8a31f2454912ba1cff4990bca7811918878091cb2ab9e52cb8"},
"plug_cowboy": {:hex, :plug_cowboy, "2.2.1", "fcf58aa33227a4322a050e4783ee99c63c031a2e7f9a2eb7340d55505e17f30f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3b43de24460d87c0971887286e7a20d40462e48eb7235954681a20cee25ddeb6"},
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
"sobelow": {:hex, :sobelow, "0.10.2", "00e91208046d3b434f9f08779fe0ca7c6d6595b7fa33b289e792dffa6dde8081", [:mix], [], "hexpm", "e30fc994330cf6f485c1c4f2fb7c4b2d403557d0e101c6e5329fd17a58e55a7e"},
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.5.0", "1b796e74add83abf844e808564275dfb342bcc930b04c7577ab780e262b0d998", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "31225e6ce7a37a421a0a96ec55244386aec1c190b22578bd245188a4a33298fd"},
"telemetry_poller": {:hex, :telemetry_poller, "0.5.0", "4770888ef85599ead39c7f51d6b4b62306e602d96c69b2625d54dea3d9a5204b", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69e4e8e65b0ae077c9e14cd5f42c7cc486de0e07ac6e3409e6f0e52699a7872c"},
}
Loading