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

[BUG] Observer crashes if the process/port doesn't exist anymore #90

Merged
merged 2 commits into from
Dec 5, 2024
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
1 change: 1 addition & 0 deletions lib/deployex/observer/port.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule Deployex.Observer.Port do
...> [h | _] = :erlang.ports()
...> assert %{connected: _, id: _, name: _, os_pid: _} = Port.info(h)
...> assert :undefined = Port.info(nil)
...> assert :undefined = Port.info("")
"""
@spec info(atom(), port()) ::
:undefined | %{connected: any(), id: any(), name: any(), os_pid: any()}
Expand Down
35 changes: 35 additions & 0 deletions lib/deployex_web/live/observer/attention.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule DeployexWeb.Observer.Attention do
@moduledoc false
use Phoenix.Component

attr :message, :string, required: true

def content(assigns) do
~H"""
<div class="flex items-center mt-1">
<div
id="live-tracing-alert"
class="p-2 border-l-8 border-red-400 rounded-lg bg-gray-300 text-red-500"
role="alert"
>
<div class="flex items-center">
<div class="flex items-center py-8">
<svg
class="flex-shrink-0 w-4 h-4 me-2"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z" />
</svg>
<span class="sr-only">Info</span>
<h3 class="text-sm font-medium">Attention</h3>
</div>
<div class="ml-2 mr-2 mt-2 mb-2 text-xs"><%= @message %></div>
</div>
</div>
</div>
"""
end
end
4 changes: 2 additions & 2 deletions lib/deployex_web/live/observer/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ defmodule DeployexWeb.ObserverLive do
</div>
</div>
<%= if @current_selected_id.type == "pid" do %>
<Process.content info={@current_selected_id.info} />
<Process.content id={@current_selected_id.id_string} info={@current_selected_id.info} />
<% else %>
<Port.content info={@current_selected_id.info} />
<Port.content id={@current_selected_id.id_string} info={@current_selected_id.info} />
<% end %>
</div>
</div>
Expand Down
35 changes: 22 additions & 13 deletions lib/deployex_web/live/observer/port.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ defmodule DeployexWeb.Observer.Port do
use DeployexWeb, :html
use Phoenix.Component

alias DeployexWeb.Observer.Attention

attr :info, :map, required: true
attr :id, :map, required: true

def content(assigns) do
info = assigns.info

port_overview =
if info do
if is_map(info) do
[
%{name: "Id", value: "#{info.id}"},
%{name: "Name", value: "#{info.name}"},
Expand All @@ -27,18 +30,24 @@ defmodule DeployexWeb.Observer.Port do

~H"""
<div class="max-w-full rounded overflow-hidden shadow-lg">
<div :if={@info != nil} id="port_information">
<div class="flex grid grid-cols-3 gap-1 items-top">
<.table_process id="port-overview-table" title="Overview" rows={@port_overview}>
<:col :let={item}>
<span><%= item.name %></span>
</:col>
<:col :let={item}>
<%= item.value %>
</:col>
</.table_process>
</div>
</div>
<%= cond do %>
<% @info == nil -> %>
<% @info == :undefined -> %>
<Attention.content message={"Port #{@id} is either dead or protected and therefore can not be shown."} />
<% true -> %>
<div id="port_information">
<div class="flex grid grid-cols-3 gap-1 items-top">
<.table_process id="port-overview-table" title="Overview" rows={@port_overview}>
<:col :let={item}>
<span><%= item.name %></span>
</:col>
<:col :let={item}>
<%= item.value %>
</:col>
</.table_process>
</div>
</div>
<% end %>
</div>
"""
end
Expand Down
110 changes: 59 additions & 51 deletions lib/deployex_web/live/observer/process.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,39 @@ defmodule DeployexWeb.Observer.Process do
use DeployexWeb, :html
use Phoenix.Component

alias DeployexWeb.Observer.Attention

attr :info, :map, required: true
attr :id, :map, required: true

def content(assigns) do
info = assigns.info

process_overview =
if info do
[
%{name: "Id", value: "#{inspect(info.pid)}"},
%{name: "Registered name", value: "#{info.registered_name}"},
%{name: "Status", value: "#{info.meta.status}"},
%{name: "Class", value: "#{info.meta.class}"},
%{name: "Message Queue Length", value: "#{info.message_queue_len}"},
%{name: "Group Leader", value: "#{inspect(info.relations.group_leader)}"},
%{name: "Trap exit", value: "#{info.trap_exit}"}
]
else
nil
end
{process_overview, process_memory} =
if is_map(info) do
process_overview =
[
%{name: "Id", value: "#{inspect(info.pid)}"},
%{name: "Registered name", value: "#{info.registered_name}"},
%{name: "Status", value: "#{info.meta.status}"},
%{name: "Class", value: "#{info.meta.class}"},
%{name: "Message Queue Length", value: "#{info.message_queue_len}"},
%{name: "Group Leader", value: "#{inspect(info.relations.group_leader)}"},
%{name: "Trap exit", value: "#{info.trap_exit}"}
]

process_memory =
if info do
[
%{name: "Total", value: "#{info.memory.total}"},
%{name: "Heap Size", value: "#{info.memory.heap_size}"},
%{name: "Stack Size", value: "#{info.memory.stack_size}"},
%{name: "GC Min Heap Size", value: "#{info.memory.gc_min_heap_size}"},
%{name: "GC FullSweep After", value: "#{info.memory.gc_full_sweep_after}"}
]
process_memory =
[
%{name: "Total", value: "#{info.memory.total}"},
%{name: "Heap Size", value: "#{info.memory.heap_size}"},
%{name: "Stack Size", value: "#{info.memory.stack_size}"},
%{name: "GC Min Heap Size", value: "#{info.memory.gc_min_heap_size}"},
%{name: "GC FullSweep After", value: "#{info.memory.gc_full_sweep_after}"}
]

{process_overview, process_memory}
else
nil
{nil, nil}
end

assigns =
Expand All @@ -44,37 +46,43 @@ defmodule DeployexWeb.Observer.Process do

~H"""
<div class="max-w-full rounded overflow-hidden shadow-lg">
<div :if={@info != nil} id="process_information">
<div class="flex grid grid-cols-3 gap-1 items-top">
<.table_process id="process-overview-table" title="Overview" rows={@process_overview}>
<:col :let={item}>
<span><%= item.name %></span>
</:col>
<:col :let={item}>
<%= item.value %>
</:col>
</.table_process>
<%= cond do %>
<% @info == nil -> %>
<% @info == :undefined -> %>
<Attention.content message={"Process #{@id} is either dead or protected and therefore can not be shown."} />
<% true -> %>
<div id="process_information">
<div class="flex grid grid-cols-3 gap-1 items-top">
<.table_process id="process-overview-table" title="Overview" rows={@process_overview}>
<:col :let={item}>
<span><%= item.name %></span>
</:col>
<:col :let={item}>
<%= item.value %>
</:col>
</.table_process>

<.table_process id="process-memory-table" title="Memory" rows={@process_memory}>
<:col :let={item}>
<span><%= item.name %></span>
</:col>
<:col :let={item}>
<%= item.value %>
</:col>
</.table_process>
<.table_process id="process-memory-table" title="Memory" rows={@process_memory}>
<:col :let={item}>
<span><%= item.name %></span>
</:col>
<:col :let={item}>
<%= item.value %>
</:col>
</.table_process>

<.relations title="State" value={"#{inspect(@info.state)}"} />
</div>
<.relations title="State" value={"#{inspect(@info.state)}"} />
</div>

<div class="flex grid grid-cols-4 mt-1 gap-1 items-top">
<.relations title="Links" value={"#{inspect(@info.relations.links)}"} />
<div class="flex grid grid-cols-4 mt-1 gap-1 items-top">
<.relations title="Links" value={"#{inspect(@info.relations.links)}"} />

<.relations title="Ancestors" value={"#{inspect(@info.relations.ancestors)}" } />
<.relations title="Monitors" value={"#{inspect(@info.relations.monitors)}"} />
<.relations title="Monitored by" value={"#{inspect(@info.relations.monitored_by)}"} />
</div>
</div>
<.relations title="Ancestors" value={"#{inspect(@info.relations.ancestors)}" } />
<.relations title="Monitors" value={"#{inspect(@info.relations.monitors)}"} />
<.relations title="Monitored by" value={"#{inspect(@info.relations.monitored_by)}"} />
</div>
</div>
<% end %>
</div>
"""
end
Expand Down
74 changes: 74 additions & 0 deletions test/deployex_web/live/observer/index_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,43 @@ defmodule DeployexWeb.Observer.IndexTest do
refute html =~ "Connected"
end

test "Select Service+Apps and select a process that is dead or doesn't exist", %{conn: conn} 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
|> 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()

series_name = "#{Node.self()}::kernel"

id = "#PID<0.0.11111>"

html =
index_live
|> element("#observer-tree")
|> render_hook("request-process", %{"id" => id, "series_name" => series_name})

# Check the Process information is not being shown
assert html =~ "Process #PID&lt;0.0.11111&gt; is either dead or protected"
end

test "Select Service+Apps and select a port to request information", %{conn: conn} do
node = Node.self() |> to_string
service = String.replace(node, "@", "-")
Expand Down Expand Up @@ -250,6 +287,43 @@ defmodule DeployexWeb.Observer.IndexTest do
assert html =~ "Connected"
end

test "Select Service+Apps and select a port that is dead or doesn't exist", %{conn: conn} 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
|> 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()

series_name = "#{Node.self()}::kernel"

id = "#Port<0.100>"

html =
index_live
|> element("#observer-tree")
|> render_hook("request-process", %{"id" => id, "series_name" => series_name})

# Check the Port information is not being shown
assert html =~ "Port #Port&lt;0.100&gt; is either dead or protected"
end

test "Select Service+Apps and select a reference to request information", %{conn: conn} do
node = Node.self() |> to_string
service = String.replace(node, "@", "-")
Expand Down