Skip to content

Commit

Permalink
Adding Rpc.call to the observer files and also increased unit test to…
Browse files Browse the repository at this point in the history
… 100%
  • Loading branch information
thiagoesteves committed Nov 26, 2024
1 parent 721ea49 commit 2524308
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 36 deletions.
30 changes: 11 additions & 19 deletions lib/deployex/observer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Deployex.Observer do
require Logger

alias Deployex.Observer.Helper
alias Deployex.Rpc

@link_line_color "#CCC"
@monitor_line_color "#D1A1E5"
Expand Down Expand Up @@ -46,33 +47,24 @@ defmodule Deployex.Observer do

@doc """
Lists all running applications.
iex> alias Deployex.Observer
...> assert Enum.find(Observer.list(), &(&1.name == :kernel))
"""
@spec list(node :: atom()) :: list({atom, String.t(), String.t()})
def list(node \\ Node.self()) do
:rpc.call(node, :application_controller, :which_applications, [])
Rpc.call(node, :application_controller, :which_applications, [], :infinity)
|> Enum.filter(&alive?(node, &1))
|> Enum.map(&structure_application/1)
end

@doc """
Retreives information about the application and its respective linked processes, ports and references.
iex> alias Deployex.Observer
...> assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} = Observer.info()
...> assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} = Observer.info(Node.self(), :deployex)
...> assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} = Observer.info(Node.self(), :phoenix_pubsub)
...> assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} = Observer.info(Node.self(), :logger)
"""
@spec info(node :: atom(), app :: atom) :: map
def info(node \\ Node.self(), app \\ :kernel) do
app_pid = :rpc.call(node, :application_controller, :get_master, [app])
app_pid = Rpc.call(node, :application_controller, :get_master, [app], :infinity)

children =
node
|> :rpc.call(:application_master, :get_child, [app_pid])
|> Rpc.call(:application_master, :get_child, [app_pid], :infinity)
|> structure_id(app_pid)

new(%{
Expand All @@ -89,7 +81,7 @@ defmodule Deployex.Observer do

defp alive?(node, {app, _, _}) do
node
|> :rpc.call(:application_controller, :get_master, [app])
|> Rpc.call(:application_controller, :get_master, [app], :infinity)
|> is_pid
catch
# coveralls-ignore-start
Expand All @@ -107,7 +99,7 @@ defmodule Deployex.Observer do
end

defp structure_id({pid, name}, parent) do
{_, dictionary} = :rpc.pinfo(pid, :dictionary)
{_, dictionary} = Rpc.pinfo(pid, :dictionary)

case Keyword.get(dictionary, :"$ancestors") do
[ancestor_parent] ->
Expand All @@ -133,7 +125,7 @@ defmodule Deployex.Observer do
defp structure_id({_, :undefined, _, _}, _parent), do: nil

defp structure_id({_, pid, :supervisor, _}, parent) do
{:links, links} = :rpc.pinfo(pid, :links)
{:links, links} = Rpc.pinfo(pid, :links)

links = links -- [parent]

Expand All @@ -153,9 +145,9 @@ defmodule Deployex.Observer do
end

defp structure_id({_, pid, :worker, _}, parent) do
{:links, links} = :rpc.pinfo(pid, :links)
{:monitored_by, monitored_by_pids} = :rpc.pinfo(pid, :monitored_by)
{:monitors, monitors} = :rpc.pinfo(pid, :monitors)
{:links, links} = Rpc.pinfo(pid, :links)
{:monitored_by, monitored_by_pids} = Rpc.pinfo(pid, :monitored_by)
{:monitors, monitors} = Rpc.pinfo(pid, :monitors)

links = links -- [parent]

Expand Down Expand Up @@ -257,7 +249,7 @@ defmodule Deployex.Observer do
# coveralls-ignore-stop

defp name(pid) when is_pid(pid) do
case :rpc.pinfo(pid, :registered_name) do
case Rpc.pinfo(pid, :registered_name) do
{_, registered_name} -> to_string(registered_name) |> String.trim_leading("Elixir.")
_ -> pid |> inspect |> String.trim_leading("#PID")
end
Expand Down
13 changes: 2 additions & 11 deletions lib/deployex/observer/process.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Deployex.Observer.Process do
"""

alias Deployex.Observer.Helper
alias Deployex.Rpc

@process_full [
:registered_name,
Expand All @@ -29,16 +30,6 @@ defmodule Deployex.Observer.Process do

@doc """
Creates a complete overview of process stats based on the given `pid`.
iex> alias Deployex.Observer.Process, as: ObserverPort
...> kernel_pid = :application_controller.get_master :kernel
...> assert %{error_handler: :error_handler, memory: _, relations: %{links: [head, tail]}} = ObserverPort.info(kernel_pid)
...> assert %{error_handler: :error_handler, memory: _, relations: _} = ObserverPort.info(head)
...> assert %{error_handler: :error_handler, memory: _, relations: _} = ObserverPort.info(tail)
...> invalid_pid = "<0.11111.0>" |> String.to_charlist() |> :erlang.list_to_pid()
...> assert :undefined = ObserverPort.info(invalid_pid)
...> supervisor = Process.whereis Elixir.Deployex.Supervisor
...> assert %{error_handler: :error_handler, memory: _, relations: _} = ObserverPort.info(supervisor)
"""
@spec info(pid :: pid()) :: :undefined | map
def info(pid) do
Expand All @@ -49,7 +40,7 @@ defmodule Deployex.Observer.Process do
### Private functions
### ==========================================================================
defp process_info(pid, information, structurer) do
case :rpc.pinfo(pid, information) do
case Rpc.pinfo(pid, information) do
:undefined -> :undefined
data -> structurer.(data, pid)
end
Expand Down
16 changes: 14 additions & 2 deletions lib/deployex/rpc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,20 @@ defmodule Deployex.Rpc do
args :: list,
timeout :: 0..4_294_967_295 | :infinity
) :: any() | {:badrpc, any()}
def call(atom, module, function, args, timeout),
do: default().call(atom, module, function, args, timeout)
def call(node, module, function, args, timeout),
do: default().call(node, module, function, args, timeout)

@doc """
Call :rpc.pinfo to request process information (local or remote pid)
Location transparent version of the BIF :erlang.process_info/2
https://www.erlang.org/doc/apps/kernel/rpc.html#pinfo/1
"""
@impl true
@spec pinfo(pid :: pid, information :: list | atom()) :: any() | :undefined
def pinfo(pid, information),
do: default().pinfo(pid, information)

### ==========================================================================
### Private functions
Expand Down
2 changes: 2 additions & 0 deletions lib/deployex/rpc/adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ defmodule Deployex.Rpc.Adapter do
args :: list,
timeout :: 0..4_294_967_295 | :infinity
) :: any() | {:badrpc, any()}

@callback pinfo(pid :: pid, information :: list | atom()) :: any() | :undefined
end
5 changes: 5 additions & 0 deletions lib/deployex/rpc/local.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ defmodule Deployex.Rpc.Local do
def call(node, module, function, args, timeout) do
:rpc.call(node, module, function, args, timeout)
end

@impl true
def pinfo(pid, information) do
:rpc.pinfo(pid, information)
end
end
4 changes: 4 additions & 0 deletions lib/deployex_web/live/observer/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,9 @@ defmodule DeployexWeb.ObserverLive do

update_observer_data(acc, data_key, %{"transition" => false, "data" => info})
else
# coveralls-ignore-start
update_observer_data(acc, data_key, nil)
# coveralls-ignore-stop
end
end)

Expand Down Expand Up @@ -313,7 +315,9 @@ defmodule DeployexWeb.ObserverLive do

update_observer_data(acc, data_key, %{"transition" => false, "data" => info})
else
# coveralls-ignore-start
update_observer_data(acc, data_key, nil)
# coveralls-ignore-stop
end
end)

Expand Down
27 changes: 25 additions & 2 deletions test/deployex/observer/process_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
defmodule Deployex.Observer.ProcessTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false

doctest Deployex.Observer.Process
import Mox

alias Deployex.Observer.Process, as: ObserverPort

setup :verify_on_exit!

test "info/1" do
Deployex.RpcMock
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

kernel_pid = :application_controller.get_master(:kernel)

assert %{error_handler: :error_handler, memory: _, relations: %{links: [head, tail]}} =
ObserverPort.info(kernel_pid)

assert %{error_handler: :error_handler, memory: _, relations: _} = ObserverPort.info(head)
assert %{error_handler: :error_handler, memory: _, relations: _} = ObserverPort.info(tail)
invalid_pid = "<0.11111.0>" |> String.to_charlist() |> :erlang.list_to_pid()
assert :undefined = ObserverPort.info(invalid_pid)
supervisor = Process.whereis(Elixir.Deployex.Supervisor)

assert %{error_handler: :error_handler, memory: _, relations: _} =
ObserverPort.info(supervisor)
end
end
35 changes: 34 additions & 1 deletion test/deployex/observer_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
defmodule Deployex.ObserverTest do
use ExUnit.Case, async: true

doctest Deployex.Observer
import Mox

alias Deployex.Observer

setup :verify_on_exit!

test "list/0" do
Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)

assert Enum.find(Observer.list(), &(&1.name == :kernel))
end

test "info/0" do
Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} =
Observer.info()

assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} =
Observer.info(Node.self(), :deployex)

assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} =
Observer.info(Node.self(), :phoenix_pubsub)

assert %Deployex.Observer{id: _, name: _, children: _, symbol: _, lineStyle: _, itemStyle: _} =
Observer.info(Node.self(), :logger)
end
end
75 changes: 74 additions & 1 deletion test/deployex_web/live/observer/index_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ defmodule DeployexWeb.Observer.IndexTest do
end

test "GET /observer", %{conn: conn} do
Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

{:ok, _index_live, html} = live(conn, ~p"/observer")

assert html =~ "Live Observer"
Expand All @@ -40,6 +46,12 @@ defmodule DeployexWeb.Observer.IndexTest do
node = Node.self() |> to_string
service = String.replace(node, "@", "-")

Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

{:ok, index_live, _html} = live(conn, ~p"/observer")

index_live
Expand Down Expand Up @@ -79,6 +91,12 @@ defmodule DeployexWeb.Observer.IndexTest do
node = Node.self() |> to_string
service = String.replace(node, "@", "-")

Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

{:ok, index_live, _html} = live(conn, ~p"/observer")

index_live
Expand Down Expand Up @@ -118,6 +136,12 @@ defmodule DeployexWeb.Observer.IndexTest do
node = Node.self() |> to_string
service = String.replace(node, "@", "-")

Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

{:ok, index_live, _html} = live(conn, ~p"/observer")

index_live
Expand Down Expand Up @@ -154,6 +178,12 @@ defmodule DeployexWeb.Observer.IndexTest do
node = Node.self() |> to_string
service = String.replace(node, "@", "-")

Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

{:ok, index_live, _html} = live(conn, ~p"/observer")

index_live
Expand All @@ -173,7 +203,7 @@ defmodule DeployexWeb.Observer.IndexTest do
html =
index_live
|> element("#observer-tree")
|> render_hook("request-process", %{"pid" => "#{inspect(port)}"})
|> render_hook("request-process", %{"id" => "#{inspect(port)}"})

# Check the Port information is NOT being shown
refute html =~ "Group Leader"
Expand All @@ -185,6 +215,12 @@ defmodule DeployexWeb.Observer.IndexTest do
node = Node.self() |> to_string
service = String.replace(node, "@", "-")

Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

{:ok, index_live, _html} = live(conn, ~p"/observer")

index_live
Expand Down Expand Up @@ -242,4 +278,41 @@ defmodule DeployexWeb.Observer.IndexTest do

assert_receive {:handle_ref_event, ^ref}, 1_000
end

test "Testing NodeUp/NodeDown", %{conn: conn} do
fake_node = :myapp@nohost
node = Node.self() |> to_string
service = String.replace(node, "@", "-")
test_pid_process = self()

Deployex.RpcMock
|> stub(:call, fn node, module, function, args, timeout ->
send(test_pid_process, {:observer_index_pid, self()})
:rpc.call(node, module, function, args, timeout)
end)
|> stub(:pinfo, fn pid, information -> :rpc.pinfo(pid, information) end)

{:ok, index_live, _html} = live(conn, ~p"/observer")

assert_receive {:observer_index_pid, observer_index_pid}, 1_000

index_live
|> element("#observer-multi-select-toggle-options")
|> render_click()

index_live
|> element("#observer-multi-select-apps-kernel-add-item")
|> render_click()

index_live
|> element("#observer-multi-select-services-#{service}-add-item")
|> render_click()

send(observer_index_pid, {:nodeup, fake_node})
send(observer_index_pid, {:nodedown, fake_node})

index_live
|> element("#observer-multi-select-apps-kernel-remove-item")
|> render_click()
end
end

0 comments on commit 2524308

Please sign in to comment.