mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-01-01 12:41:43 +08:00
List connected distribution nodes in the runtime panel (#2636)
This commit is contained in:
parent
87daabaf60
commit
68fa363d2d
14 changed files with 256 additions and 42 deletions
|
@ -244,6 +244,11 @@ solely client-side operations.
|
|||
@apply hidden;
|
||||
}
|
||||
|
||||
[data-el-session][data-js-side-panel-content="runtime-info"]
|
||||
[data-el-runtime-indicator] {
|
||||
@apply border-gray-700;
|
||||
}
|
||||
|
||||
[data-el-session]:not([data-js-side-panel-content="app-info"])
|
||||
[data-el-app-info] {
|
||||
@apply hidden;
|
||||
|
|
|
@ -765,6 +765,17 @@ defprotocol Livebook.Runtime do
|
|||
|
||||
@type proxy_handler_spec :: {module :: module(), function :: atom(), args :: list()}
|
||||
|
||||
@typedoc """
|
||||
An information about Elixir nodes that the runtime is connected to.
|
||||
|
||||
Whenever the node list change, the runtime should send an updated
|
||||
list as:
|
||||
|
||||
* `{:runtime_connected_nodes, connected_nodes()}`
|
||||
|
||||
"""
|
||||
@type connected_nodes :: list(node())
|
||||
|
||||
@doc """
|
||||
Returns relevant information about the runtime.
|
||||
|
||||
|
@ -1116,4 +1127,13 @@ defprotocol Livebook.Runtime do
|
|||
"""
|
||||
@spec fetch_proxy_handler_spec(t()) :: {:ok, proxy_handler_spec()} | {:error, :not_found}
|
||||
def fetch_proxy_handler_spec(runtime)
|
||||
|
||||
@doc """
|
||||
Asks the runtime to disconnect from the given connected node.
|
||||
|
||||
The node should be one of `connected_nodes()` reported by the runtime
|
||||
earlier.
|
||||
"""
|
||||
@spec disconnect_node(t(), node()) :: :ok
|
||||
def disconnect_node(runtime, node)
|
||||
end
|
||||
|
|
|
@ -208,4 +208,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Attached do
|
|||
def fetch_proxy_handler_spec(runtime) do
|
||||
RuntimeServer.fetch_proxy_handler_spec(runtime.server_pid)
|
||||
end
|
||||
|
||||
def disconnect_node(runtime, node) do
|
||||
RuntimeServer.disconnect_node(runtime.server_pid, node)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -327,4 +327,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.ElixirStandalone do
|
|||
def fetch_proxy_handler_spec(runtime) do
|
||||
RuntimeServer.fetch_proxy_handler_spec(runtime.server_pid)
|
||||
end
|
||||
|
||||
def disconnect_node(runtime, node) do
|
||||
RuntimeServer.disconnect_node(runtime.server_pid, node)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -175,6 +175,10 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Embedded do
|
|||
RuntimeServer.fetch_proxy_handler_spec(runtime.server_pid)
|
||||
end
|
||||
|
||||
def disconnect_node(runtime, node) do
|
||||
RuntimeServer.disconnect_node(runtime.server_pid, node)
|
||||
end
|
||||
|
||||
defp config() do
|
||||
Application.get_env(:livebook, Livebook.Runtime.Embedded, [])
|
||||
end
|
||||
|
|
|
@ -328,6 +328,10 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
|||
end
|
||||
end
|
||||
|
||||
def disconnect_node(pid, node) do
|
||||
GenServer.cast(pid, {:disconnect_node, node})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Stops the runtime server.
|
||||
|
||||
|
@ -362,10 +366,11 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
|||
smart_cell_supervisor: nil,
|
||||
smart_cell_gl: nil,
|
||||
smart_cells: %{},
|
||||
smart_cell_definitions: nil,
|
||||
smart_cell_definitions: [],
|
||||
smart_cell_definitions_module:
|
||||
Keyword.get(opts, :smart_cell_definitions_module, Kino.SmartCell),
|
||||
extra_smart_cell_definitions: Keyword.get(opts, :extra_smart_cell_definitions, []),
|
||||
connected_nodes: [],
|
||||
memory_timer_ref: nil,
|
||||
last_evaluator: nil,
|
||||
base_env_path:
|
||||
|
@ -408,7 +413,7 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
|||
def handle_info({:evaluation_finished, locator}, state) do
|
||||
{:noreply,
|
||||
state
|
||||
|> report_smart_cell_definitions()
|
||||
|> report_environment()
|
||||
|> report_transient_state()
|
||||
|> scan_binding_after_evaluation(locator)}
|
||||
end
|
||||
|
@ -480,7 +485,7 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
|||
Process.monitor(owner)
|
||||
|
||||
state = %{state | owner: owner, runtime_broadcast_to: opts[:runtime_broadcast_to]}
|
||||
state = report_smart_cell_definitions(state)
|
||||
state = report_environment(state)
|
||||
report_memory_usage(state)
|
||||
|
||||
{:ok, smart_cell_supervisor} = DynamicSupervisor.start_link(strategy: :one_for_one)
|
||||
|
@ -710,6 +715,11 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
|||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_cast({:disconnect_node, node}, state) do
|
||||
Node.disconnect(node)
|
||||
{:noreply, report_connected_nodes(state)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:read_file, path}, {from_pid, _}, state) do
|
||||
# Delegate reading to a separate task and let the caller
|
||||
|
@ -817,6 +827,12 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
|||
send(state.owner, {:runtime_memory_usage, Evaluator.memory()})
|
||||
end
|
||||
|
||||
defp report_environment(state) do
|
||||
state
|
||||
|> report_smart_cell_definitions()
|
||||
|> report_connected_nodes()
|
||||
end
|
||||
|
||||
defp report_smart_cell_definitions(state) do
|
||||
smart_cell_definitions = get_smart_cell_definitions(state.smart_cell_definitions_module)
|
||||
|
||||
|
@ -837,6 +853,19 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
|||
end
|
||||
end
|
||||
|
||||
defp report_connected_nodes(state) do
|
||||
owner_node = node(state.owner)
|
||||
nodes = Node.list(:connected) |> List.delete(owner_node) |> Enum.sort()
|
||||
|
||||
if nodes == state.connected_nodes do
|
||||
state
|
||||
else
|
||||
send(state.owner, {:runtime_connected_nodes, nodes})
|
||||
|
||||
%{state | connected_nodes: nodes}
|
||||
end
|
||||
end
|
||||
|
||||
defp get_smart_cell_definitions(module) do
|
||||
if Code.ensure_loaded?(module) and function_exported?(module, :definitions, 0) do
|
||||
module.definitions()
|
||||
|
|
|
@ -1735,6 +1735,11 @@ defmodule Livebook.Session do
|
|||
{:noreply, state |> put_memory_usage(runtime_memory) |> notify_update()}
|
||||
end
|
||||
|
||||
def handle_info({:runtime_connected_nodes, nodes}, state) do
|
||||
operation = {:set_runtime_connected_nodes, @client_id, nodes}
|
||||
{:noreply, handle_operation(state, operation)}
|
||||
end
|
||||
|
||||
def handle_info({:runtime_smart_cell_definitions, definitions}, state) do
|
||||
operation = {:set_smart_cell_definitions, @client_id, definitions}
|
||||
{:noreply, handle_operation(state, operation)}
|
||||
|
|
|
@ -28,6 +28,7 @@ defmodule Livebook.Session.Data do
|
|||
:bin_entries,
|
||||
:runtime,
|
||||
:runtime_transient_state,
|
||||
:runtime_connected_nodes,
|
||||
:smart_cell_definitions,
|
||||
:clients_map,
|
||||
:users_map,
|
||||
|
@ -55,6 +56,7 @@ defmodule Livebook.Session.Data do
|
|||
bin_entries: list(cell_bin_entry()),
|
||||
runtime: Runtime.t(),
|
||||
runtime_transient_state: Runtime.transient_state(),
|
||||
runtime_connected_nodes: list(node()),
|
||||
smart_cell_definitions: list(Runtime.smart_cell_definition()),
|
||||
clients_map: %{client_id() => User.id()},
|
||||
users_map: %{User.id() => User.t()},
|
||||
|
@ -214,6 +216,7 @@ defmodule Livebook.Session.Data do
|
|||
| {:set_input_value, client_id(), input_id(), value :: term()}
|
||||
| {:set_runtime, client_id(), Runtime.t()}
|
||||
| {:set_runtime_transient_state, client_id(), Runtime.transient_state()}
|
||||
| {:set_runtime_connected_nodes, client_id(), list(node())}
|
||||
| {:set_smart_cell_definitions, client_id(), list(Runtime.smart_cell_definition())}
|
||||
| {:set_file, client_id(), FileSystem.File.t() | nil}
|
||||
| {:set_autosave_interval, client_id(), non_neg_integer() | nil}
|
||||
|
@ -303,6 +306,7 @@ defmodule Livebook.Session.Data do
|
|||
bin_entries: [],
|
||||
runtime: default_runtime,
|
||||
runtime_transient_state: %{},
|
||||
runtime_connected_nodes: [],
|
||||
smart_cell_definitions: [],
|
||||
clients_map: %{},
|
||||
users_map: %{},
|
||||
|
@ -871,6 +875,13 @@ defmodule Livebook.Session.Data do
|
|||
|> wrap_ok()
|
||||
end
|
||||
|
||||
def apply_operation(data, {:set_runtime_connected_nodes, _client_id, nodes}) do
|
||||
data
|
||||
|> with_actions()
|
||||
|> set_runtime_connected_nodes(nodes)
|
||||
|> wrap_ok()
|
||||
end
|
||||
|
||||
def apply_operation(data, {:set_smart_cell_definitions, _client_id, definitions}) do
|
||||
data
|
||||
|> with_actions()
|
||||
|
@ -1955,7 +1966,13 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
|
||||
defp set_runtime(data_actions, prev_data, runtime) do
|
||||
{data, _} = data_actions = set!(data_actions, runtime: runtime, smart_cell_definitions: [])
|
||||
{data, _} =
|
||||
data_actions =
|
||||
set!(data_actions,
|
||||
runtime: runtime,
|
||||
runtime_connected_nodes: [],
|
||||
smart_cell_definitions: []
|
||||
)
|
||||
|
||||
if not Runtime.connected?(prev_data.runtime) and Runtime.connected?(data.runtime) do
|
||||
data_actions
|
||||
|
@ -2006,6 +2023,12 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
end
|
||||
|
||||
defp set_runtime_connected_nodes(data_actions, nodes) do
|
||||
data_actions
|
||||
|> set!(runtime_connected_nodes: nodes)
|
||||
|> maybe_start_smart_cells()
|
||||
end
|
||||
|
||||
defp set_smart_cell_definitions(data_actions, smart_cell_definitions) do
|
||||
data_actions
|
||||
|> set!(smart_cell_definitions: smart_cell_definitions)
|
||||
|
|
|
@ -539,9 +539,11 @@ defmodule LivebookWeb.CoreComponents do
|
|||
<span class="text-sm text-gray-500">
|
||||
<%= @label %>
|
||||
</span>
|
||||
<span class={
|
||||
"text-gray-800 text-sm font-semibold #{if @one_line, do: "whitespace-nowrap overflow-auto tiny-scrollbar"}"
|
||||
}>
|
||||
<span class={[
|
||||
"text-gray-800 text-sm font-semibold",
|
||||
@one_line &&
|
||||
"whitespace-nowrap overflow-hidden text-ellipsis hover:text-clip hover:overflow-auto hover:tiny-scrollbar"
|
||||
]}>
|
||||
<%= render_slot(@inner_block) %>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -578,6 +578,17 @@ defmodule LivebookWeb.SessionLive do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("runtime_disconnect_node", %{"node" => node}, socket) do
|
||||
node = Enum.find(socket.private.data.runtime_connected_nodes, &(Atom.to_string(&1) == node))
|
||||
runtime = socket.private.data.runtime
|
||||
|
||||
if node && Runtime.connected?(runtime) do
|
||||
Runtime.disconnect_node(runtime, node)
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("deploy_app", _, socket) do
|
||||
data = socket.private.data
|
||||
app_settings = data.notebook.app_settings
|
||||
|
@ -1778,6 +1789,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
dirty: data.dirty,
|
||||
persistence_warnings: data.persistence_warnings,
|
||||
runtime: data.runtime,
|
||||
runtime_connected_nodes: Enum.sort(data.runtime_connected_nodes),
|
||||
smart_cell_definitions: Enum.sort_by(data.smart_cell_definitions, & &1.name),
|
||||
example_snippet_definitions:
|
||||
data.runtime
|
||||
|
|
|
@ -18,7 +18,12 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
data-p-global-status={hook_prop(elem(@data_view.global_status, 0))}
|
||||
data-p-autofocus-cell-id={hook_prop(@autofocus_cell_id)}
|
||||
>
|
||||
<.sidebar app={@app} session={@session} live_action={@live_action} current_user={@current_user} />
|
||||
<.sidebar
|
||||
session={@session}
|
||||
live_action={@live_action}
|
||||
current_user={@current_user}
|
||||
runtime_connected_nodes={@data_view.runtime_connected_nodes}
|
||||
/>
|
||||
<.side_panel app={@app} session={@session} data_view={@data_view} client_id={@client_id} />
|
||||
<div class="grow overflow-y-auto relative" data-el-notebook>
|
||||
<div data-el-js-view-iframes phx-update="ignore" id="js-view-iframes"></div>
|
||||
|
@ -333,11 +338,21 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
button_attrs={["data-el-clients-list-toggle": true]}
|
||||
/>
|
||||
|
||||
<.button_item
|
||||
icon="cpu-line"
|
||||
label="Runtime settings (sr)"
|
||||
button_attrs={["data-el-runtime-info-toggle": true]}
|
||||
/>
|
||||
<div class="relative">
|
||||
<.button_item
|
||||
icon="cpu-line"
|
||||
label="Runtime settings (sr)"
|
||||
button_attrs={["data-el-runtime-info-toggle": true]}
|
||||
/>
|
||||
<div
|
||||
:if={@runtime_connected_nodes != []}
|
||||
data-el-runtime-indicator
|
||||
class={[
|
||||
"absolute w-[12px] h-[12px] border-gray-900 border-2 rounded-full right-1.5 top-1.5 pointer-events-none",
|
||||
"bg-blue-500"
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<%!-- Hub functionality --%>
|
||||
|
||||
|
@ -626,7 +641,7 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
</.icon_button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-col mt-2 space-y-4">
|
||||
<div class="flex flex-col mt-2">
|
||||
<div class="flex flex-col space-y-3">
|
||||
<.labeled_text
|
||||
:for={{label, value} <- Runtime.describe(@data_view.runtime)}
|
||||
|
@ -636,7 +651,7 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
<%= value %>
|
||||
</.labeled_text>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="mt-4 grid grid-cols-2 gap-2">
|
||||
<%= if Runtime.connected?(@data_view.runtime) do %>
|
||||
<.button phx-click="reconnect_runtime">
|
||||
<.remix_icon icon="wireless-charging-line" />
|
||||
|
@ -651,21 +666,6 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/runtime"}>
|
||||
Configure
|
||||
</.button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col pt-6 space-y-2">
|
||||
<%= if uses_memory?(@session.memory_usage) do %>
|
||||
<.memory_info memory_usage={@session.memory_usage} />
|
||||
<% else %>
|
||||
<div class="text-sm text-gray-800 flex flex-col">
|
||||
<span class="w-full uppercase font-semibold text-gray-500">Memory</span>
|
||||
<p class="py-1">
|
||||
<%= format_bytes(@session.memory_usage.system.free) %> available out of <%= format_bytes(
|
||||
@session.memory_usage.system.total
|
||||
) %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<.button
|
||||
:if={Runtime.connected?(@data_view.runtime)}
|
||||
|
@ -673,27 +673,50 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
outlined
|
||||
type="button"
|
||||
phx-click="disconnect_runtime"
|
||||
class="col-span-2"
|
||||
>
|
||||
Disconnect
|
||||
</.button>
|
||||
</div>
|
||||
|
||||
<.memory_usage_info memory_usage={@session.memory_usage} />
|
||||
|
||||
<.runtime_connected_nodes_info runtime_connected_nodes={@data_view.runtime_connected_nodes} />
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp memory_info(assigns) do
|
||||
assigns = assign(assigns, :runtime_memory, runtime_memory(assigns.memory_usage))
|
||||
|
||||
defp memory_usage_info(assigns) do
|
||||
~H"""
|
||||
<div class="flex flex-col justify-center">
|
||||
<div class="mb-1 text-sm text-gray-800 flex flex-row justify-between">
|
||||
<span class="text-gray-500 font-semibold uppercase">Memory</span>
|
||||
<span class="text-right">
|
||||
<div class="mt-8 flex flex-col gap-2">
|
||||
<div class="text-sm text-gray-800 flex flex-row justify-between">
|
||||
<span class="text-gray-500 font-semibold uppercase">
|
||||
Memory
|
||||
</span>
|
||||
<span :if={uses_memory?(@memory_usage)}>
|
||||
<%= format_bytes(@memory_usage.system.free) %> available
|
||||
</span>
|
||||
</div>
|
||||
<div class="w-full h-8 flex flex-row py-1 gap-0.5">
|
||||
<%= if uses_memory?(@memory_usage) do %>
|
||||
<.runtime_memory_info memory_usage={@memory_usage} />
|
||||
<% else %>
|
||||
<p class="text-sm text-gray-800">
|
||||
<%= format_bytes(@memory_usage.system.free) %> available out of <%= format_bytes(
|
||||
@memory_usage.system.total
|
||||
) %>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp runtime_memory_info(assigns) do
|
||||
assigns = assign(assigns, :runtime_memory, runtime_memory(assigns.memory_usage))
|
||||
|
||||
~H"""
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="w-full flex flex-row gap-0.5">
|
||||
<div
|
||||
:for={{type, memory} <- @runtime_memory}
|
||||
class={["h-6", memory_color(type)]}
|
||||
|
@ -701,15 +724,15 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col py-1">
|
||||
<div class="flex flex-col">
|
||||
<div :for={{type, memory} <- @runtime_memory} class="flex flex-row items-center">
|
||||
<span class={["w-4 h-4 mr-2 rounded", memory_color(type)]}></span>
|
||||
<span class="capitalize text-gray-700"><%= type %></span>
|
||||
<span class="text-gray-500 ml-auto"><%= memory.unit %></span>
|
||||
</div>
|
||||
<div class="flex rounded justify-center my-2 py-0.5 text-sm text-gray-800 bg-gray-200">
|
||||
Total: <%= format_bytes(@memory_usage.runtime.total) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex rounded justify-center py-0.5 text-sm text-gray-800 bg-gray-200">
|
||||
Total: <%= format_bytes(@memory_usage.runtime.total) %>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
@ -735,6 +758,41 @@ defmodule LivebookWeb.SessionLive.Render do
|
|||
end)
|
||||
end
|
||||
|
||||
defp runtime_connected_nodes_info(assigns) do
|
||||
~H"""
|
||||
<div class="mt-8 flex flex-col gap-2">
|
||||
<span class="text-sm text-gray-500 font-semibold uppercase">
|
||||
Connected nodes
|
||||
</span>
|
||||
<%= if @runtime_connected_nodes == [] do %>
|
||||
<div class="text-sm text-gray-800 flex flex-col">
|
||||
No connected nodes
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="flex flex-col">
|
||||
<div
|
||||
:for={node <- @runtime_connected_nodes}
|
||||
class="flex flex-nowrap items-baseline py-1 pl-2 -ml-2 pr-1 hover:bg-gray-100 group rounded-lg"
|
||||
>
|
||||
<.remix_icon icon="circle-fill" class="mr-2 text-xs text-blue-500" />
|
||||
<div class={[
|
||||
"flex-grow text-sm text-gray-700 text-medium whitespace-nowrap text-ellipsis overflow-hidden",
|
||||
"group-hover:overflow-visible group-hover:whitespace-normal group-hover:break-all"
|
||||
]}>
|
||||
<%= node %>
|
||||
</div>
|
||||
<span class="tooltip left" data-tooltip="Disconnect">
|
||||
<.icon_button phx-click="runtime_disconnect_node" phx-value-node={node} small>
|
||||
<.remix_icon icon="close-line" />
|
||||
</.icon_button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp section_status(%{status: :evaluating} = assigns) do
|
||||
~H"""
|
||||
<button data-el-focus-cell-button data-target={@cell_id}>
|
||||
|
|
|
@ -3836,6 +3836,23 @@ defmodule Livebook.Session.DataTest do
|
|||
|
||||
assert new_data.section_infos["setup-section"].evaluation_queue == MapSet.new([])
|
||||
end
|
||||
|
||||
test "clears runtime-related state" do
|
||||
data =
|
||||
data_after_operations!([
|
||||
{:set_smart_cell_definitions, @cid, @smart_cell_definitions},
|
||||
{:set_runtime_connected_nodes, @cid, [:node@host]}
|
||||
])
|
||||
|
||||
runtime = connected_noop_runtime()
|
||||
operation = {:set_runtime, @cid, runtime}
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
smart_cell_definitions: [],
|
||||
runtime_connected_nodes: []
|
||||
}, []} = Data.apply_operation(data, operation)
|
||||
end
|
||||
end
|
||||
|
||||
describe "apply_operation/2 given :set_runtime_transient_state" do
|
||||
|
|
|
@ -930,6 +930,32 @@ defmodule LivebookWeb.SessionLiveTest do
|
|||
assert page =~ "Reconnect"
|
||||
assert page =~ "Disconnect"
|
||||
end
|
||||
|
||||
test "disconnecting a connected node", %{conn: conn, session: session} do
|
||||
{:ok, runtime} = Livebook.Runtime.NoopRuntime.new(self()) |> Livebook.Runtime.connect()
|
||||
Session.set_runtime(session.pid, runtime)
|
||||
|
||||
{:ok, view, _} = live(conn, ~p"/sessions/#{session.id}")
|
||||
|
||||
Session.subscribe(session.id)
|
||||
|
||||
assert render(view) =~ "No connected nodes"
|
||||
|
||||
# Mimic the runtime reporting a connected node
|
||||
node = :node@host
|
||||
send(session.pid, {:runtime_connected_nodes, [node]})
|
||||
|
||||
assert_receive {:operation, {:set_runtime_connected_nodes, _pid, _nodes}}
|
||||
|
||||
refute render(view) =~ "No connected nodes"
|
||||
assert render(view) =~ "#{node}"
|
||||
|
||||
view
|
||||
|> element(~s{button[phx-click="runtime_disconnect_node"]})
|
||||
|> render_click()
|
||||
|
||||
assert_receive {:runtime_trace, :disconnect_node, [^node]}
|
||||
end
|
||||
end
|
||||
|
||||
describe "persistence settings" do
|
||||
|
|
|
@ -75,6 +75,11 @@ defmodule Livebook.Runtime.NoopRuntime do
|
|||
def unregister_clients(_, _), do: :ok
|
||||
def fetch_proxy_handler_spec(_), do: {:error, :not_found}
|
||||
|
||||
def disconnect_node(runtime, node) do
|
||||
trace(runtime, :disconnect_node, [node])
|
||||
:ok
|
||||
end
|
||||
|
||||
defp trace(runtime, fun, args) do
|
||||
if runtime.trace_to do
|
||||
send(runtime.trace_to, {:runtime_trace, fun, args})
|
||||
|
|
Loading…
Reference in a new issue