2021-03-04 05:56:28 +08:00
|
|
|
defmodule LivebookWeb.SessionLive.AttachedLive do
|
|
|
|
use LivebookWeb, :live_view
|
2021-02-27 03:53:29 +08:00
|
|
|
|
2021-03-04 05:56:28 +08:00
|
|
|
alias Livebook.{Session, Runtime, Utils}
|
2021-02-27 03:53:29 +08:00
|
|
|
|
|
|
|
@impl true
|
2021-03-20 21:10:15 +08:00
|
|
|
def mount(_params, %{"session_id" => session_id, "current_runtime" => current_runtime}, socket) do
|
|
|
|
{:ok,
|
|
|
|
assign(socket,
|
|
|
|
session_id: session_id,
|
|
|
|
error_message: nil,
|
2021-04-28 20:28:28 +08:00
|
|
|
data: initial_data(current_runtime)
|
2021-03-20 21:10:15 +08:00
|
|
|
)}
|
2021-02-27 03:53:29 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def render(assigns) do
|
|
|
|
~L"""
|
2021-04-04 18:42:46 +08:00
|
|
|
<div class="flex-col space-y-5">
|
2021-02-27 03:53:29 +08:00
|
|
|
<%= if @error_message do %>
|
2021-03-12 18:57:01 +08:00
|
|
|
<div class="mb-3 rounded-lg px-4 py-2 bg-red-100 text-red-400 font-medium">
|
2021-02-27 03:53:29 +08:00
|
|
|
<%= @error_message %>
|
|
|
|
</div>
|
|
|
|
<% end %>
|
2021-03-20 21:10:15 +08:00
|
|
|
<p class="text-gray-700">
|
2021-02-27 03:53:29 +08:00
|
|
|
Connect the session to an already running node
|
|
|
|
and evaluate code in the context of that node.
|
|
|
|
Thanks to this approach you can work with
|
|
|
|
an arbitrary Elixir runtime.
|
2021-04-28 20:28:28 +08:00
|
|
|
Make sure to give the node a name and a cookie, for example:
|
2021-02-27 03:53:29 +08:00
|
|
|
</p>
|
2021-03-20 21:10:15 +08:00
|
|
|
<div class="text-gray-700 markdown">
|
2021-03-04 05:56:28 +08:00
|
|
|
<%= if Livebook.Config.shortnames? do %>
|
2021-04-28 20:28:28 +08:00
|
|
|
<pre><code>iex --sname test --cookie mycookie</code></pre>
|
2021-02-27 03:53:29 +08:00
|
|
|
<% else %>
|
2021-04-28 20:28:28 +08:00
|
|
|
<pre><code>iex --name test@127.0.0.1 --cookie mycookie</code></pre>
|
2021-02-27 03:53:29 +08:00
|
|
|
<% end %>
|
|
|
|
</div>
|
2021-03-20 21:10:15 +08:00
|
|
|
<p class="text-gray-700">
|
2021-04-28 20:28:28 +08:00
|
|
|
Then enter the connection information below:
|
2021-02-27 03:53:29 +08:00
|
|
|
</p>
|
2021-04-28 20:28:28 +08:00
|
|
|
<%= f = form_for :data, "#", phx_submit: "init", phx_change: "validate" %>
|
|
|
|
<div class="flex flex-col space-y-4">
|
|
|
|
<div>
|
2021-05-04 02:03:19 +08:00
|
|
|
<div class="input-label">Name</div>
|
2021-04-28 20:28:28 +08:00
|
|
|
<%= text_input f, :name, value: @data["name"], class: "input",
|
|
|
|
placeholder: if(Livebook.Config.shortnames?, do: "test", else: "test@127.0.0.1") %>
|
|
|
|
</div>
|
|
|
|
<div>
|
2021-05-04 02:03:19 +08:00
|
|
|
<div class="input-label">Cookie</div>
|
2021-04-28 20:28:28 +08:00
|
|
|
<%= text_input f, :cookie, value: @data["cookie"], class: "input", placeholder: "mycookie" %>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<%= submit "Connect", class: "mt-5 button button-blue", disabled: not data_valid?(@data) %>
|
2021-02-27 03:53:29 +08:00
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2021-04-28 20:28:28 +08:00
|
|
|
def handle_event("validate", %{"data" => data}, socket) do
|
|
|
|
{:noreply, assign(socket, data: data)}
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_event("init", %{"data" => data}, socket) do
|
|
|
|
node = Utils.node_from_name(data["name"])
|
|
|
|
cookie = String.to_atom(data["cookie"])
|
2021-02-27 03:53:29 +08:00
|
|
|
|
2021-04-28 20:28:28 +08:00
|
|
|
case Runtime.Attached.init(node, cookie) do
|
2021-02-27 03:53:29 +08:00
|
|
|
{:ok, runtime} ->
|
|
|
|
Session.connect_runtime(socket.assigns.session_id, runtime)
|
2021-04-28 20:28:28 +08:00
|
|
|
{:noreply, assign(socket, data: data, error_message: nil)}
|
2021-02-27 03:53:29 +08:00
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
message = runtime_error_to_message(error)
|
2021-04-28 20:28:28 +08:00
|
|
|
{:noreply, assign(socket, data: data, error_message: message)}
|
2021-02-27 03:53:29 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-28 20:28:28 +08:00
|
|
|
defp initial_data(%Runtime.Attached{node: node, cookie: cookie}) do
|
|
|
|
%{
|
|
|
|
"name" => Atom.to_string(node),
|
|
|
|
"cookie" => Atom.to_string(cookie)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp initial_data(_runtime), do: %{"name" => "", "cookie" => ""}
|
|
|
|
|
|
|
|
defp data_valid?(data) do
|
|
|
|
data["name"] != "" and data["cookie"] != ""
|
|
|
|
end
|
2021-04-01 21:49:39 +08:00
|
|
|
|
2021-02-27 03:53:29 +08:00
|
|
|
defp runtime_error_to_message(:unreachable), do: "Node unreachable"
|
|
|
|
|
|
|
|
defp runtime_error_to_message(:already_in_use),
|
|
|
|
do: "Another session is already connected to this node"
|
|
|
|
end
|