Require cookie for connecting to an attached node (#245)

* Require cookie for connecting to an attached node

* Fix modals sizing
This commit is contained in:
Jonatan Kłosko 2021-04-28 14:28:28 +02:00 committed by GitHub
parent 5fede30764
commit 3694576244
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 74 additions and 31 deletions

View file

@ -9,23 +9,27 @@ defmodule Livebook.Runtime.Attached do
# The node can be an ordinary Elixir runtime,
# a Mix project shell, a running release or anything else.
defstruct [:node]
defstruct [:node, :cookie]
@type t :: %__MODULE__{
node: node()
node: node(),
cookie: atom()
}
@doc """
Checks if the given node is available for use and initializes
it with Livebook-specific modules and processes.
"""
@spec init(node()) :: {:ok, t()} | {:error, :unreachable | :already_in_use}
def init(node) do
@spec init(node(), atom()) :: {:ok, t()} | {:error, :unreachable | :already_in_use}
def init(node, cookie \\ Node.get_cookie()) do
# Set cookie for connecting to this specific node
Node.set_cookie(node, cookie)
case Node.ping(node) do
:pong ->
case Livebook.Runtime.ErlDist.initialize(node) do
:ok ->
{:ok, %__MODULE__{node: node}}
{:ok, %__MODULE__{node: node, cookie: cookie}}
{:error, :already_in_use} ->
{:error, :already_in_use}

View file

@ -11,7 +11,16 @@ defmodule LivebookWeb.Helpers do
"""
def live_modal(socket, component, opts) do
path = Keyword.fetch!(opts, :return_to)
modal_opts = [id: :modal, return_to: path, component: component, opts: opts]
modal_class = Keyword.get(opts, :modal_class)
modal_opts = [
id: :modal,
return_to: path,
modal_class: modal_class,
component: component,
opts: opts
]
live_component(socket, LivebookWeb.ModalComponent, modal_opts)
end

View file

@ -102,6 +102,7 @@ defmodule LivebookWeb.HomeLive do
<%= if @live_action == :close_session do %>
<%= live_modal @socket, LivebookWeb.HomeLive.CloseSessionComponent,
id: :close_session_modal,
modal_class: "w-full max-w-xl",
return_to: Routes.home_path(@socket, :page),
session_summary: @session_summary %>
<% end %>
@ -109,6 +110,7 @@ defmodule LivebookWeb.HomeLive do
<%= if @live_action == :import do %>
<%= live_modal @socket, LivebookWeb.HomeLive.ImportComponent,
id: :import_modal,
modal_class: "w-full max-w-xl",
return_to: Routes.home_path(@socket, :page),
tab: @tab %>
<% end %>

View file

@ -6,7 +6,7 @@ defmodule LivebookWeb.HomeLive.CloseSessionComponent do
@impl true
def render(assigns) do
~L"""
<div class="p-6 pb-4 max-w-xl w-screen flex flex-col space-y-8">
<div class="p-6 pb-4 flex flex-col space-y-8">
<h3 class="text-2xl font-semibold text-gray-800">
Close session
</h3>

View file

@ -4,7 +4,7 @@ defmodule LivebookWeb.HomeLive.ImportComponent do
@impl true
def render(assigns) do
~L"""
<div class="p-6 pb-4 max-w-xl w-screen flex flex-col space-y-8">
<div class="p-6 pb-4 flex flex-col space-y-8">
<h3 class="text-2xl font-semibold text-gray-800">
Import notebook
</h3>

View file

@ -19,7 +19,7 @@ defmodule LivebookWeb.ModalComponent do
phx-page-loading></div>
<!-- Modal box -->
<div class="relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl"
<div class="relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl <%= @modal_class %>"
role="dialog"
aria-modal="true">

View file

@ -173,6 +173,7 @@ defmodule LivebookWeb.SessionLive do
<%= if @live_action == :runtime_settings do %>
<%= live_modal @socket, LivebookWeb.SessionLive.RuntimeComponent,
id: :runtime_settings_modal,
modal_class: "w-full max-w-4xl",
return_to: Routes.session_path(@socket, :page, @session_id),
session_id: @session_id,
runtime: @data_view.runtime %>
@ -181,6 +182,7 @@ defmodule LivebookWeb.SessionLive do
<%= if @live_action == :file_settings do %>
<%= live_modal @socket, LivebookWeb.SessionLive.PersistenceComponent,
id: :runtime_settings_modal,
modal_class: "w-full max-w-4xl",
return_to: Routes.session_path(@socket, :page, @session_id),
session_id: @session_id,
current_path: @data_view.path,
@ -190,6 +192,7 @@ defmodule LivebookWeb.SessionLive do
<%= if @live_action == :shortcuts do %>
<%= live_modal @socket, LivebookWeb.SessionLive.ShortcutsComponent,
id: :shortcuts_modal,
modal_class: "w-full max-w-5xl",
platform: @platform,
return_to: Routes.session_path(@socket, :page, @session_id) %>
<% end %>
@ -197,6 +200,7 @@ defmodule LivebookWeb.SessionLive do
<%= if @live_action == :cell_settings do %>
<%= live_modal @socket, LivebookWeb.SessionLive.CellSettingsComponent,
id: :cell_settings_modal,
modal_class: "w-full max-w-xl",
session_id: @session_id,
cell: @cell,
return_to: Routes.session_path(@socket, :page, @session_id) %>
@ -205,6 +209,7 @@ defmodule LivebookWeb.SessionLive do
<%= if @live_action == :cell_upload do %>
<%= live_modal @socket, LivebookWeb.SessionLive.CellUploadComponent,
id: :cell_upload_modal,
modal_class: "w-full max-w-xl",
session_id: @session_id,
cell: @cell,
uploads: @uploads,

View file

@ -9,7 +9,7 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
assign(socket,
session_id: session_id,
error_message: nil,
name: initial_name(current_runtime)
data: initial_data(current_runtime)
)}
end
@ -27,45 +27,68 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
and evaluate code in the context of that node.
Thanks to this approach you can work with
an arbitrary Elixir runtime.
Make sure to give the node a name, for example:
Make sure to give the node a name and a cookie, for example:
</p>
<div class="text-gray-700 markdown">
<%= if Livebook.Config.shortnames? do %>
<pre><code>iex --sname test</code></pre>
<pre><code>iex --sname test --cookie mycookie</code></pre>
<% else %>
<pre><code>iex --name test@127.0.0.1</code></pre>
<pre><code>iex --name test@127.0.0.1 --cookie mycookie</code></pre>
<% end %>
</div>
<p class="text-gray-700">
Then enter the name of the node below:
Then enter the connection information below:
</p>
<%= f = form_for :node, "#", phx_submit: "init" %>
<%= text_input f, :name, value: @name, class: "input",
placeholder: if(Livebook.Config.shortnames?, do: "test", else: "test@127.0.0.1") %>
<%= submit "Connect", class: "mt-5 button button-blue" %>
<%= f = form_for :data, "#", phx_submit: "init", phx_change: "validate" %>
<div class="flex flex-col space-y-4">
<div>
<div class="mb-0.5 text-sm text-gray-800 font-medium">Name</div>
<%= text_input f, :name, value: @data["name"], class: "input",
placeholder: if(Livebook.Config.shortnames?, do: "test", else: "test@127.0.0.1") %>
</div>
<div>
<div class="mb-0.5 text-sm text-gray-800 font-medium">Cookie</div>
<%= 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) %>
</form>
</div>
"""
end
@impl true
def handle_event("init", %{"node" => %{"name" => name}}, socket) do
node = Utils.node_from_name(name)
def handle_event("validate", %{"data" => data}, socket) do
{:noreply, assign(socket, data: data)}
end
case Runtime.Attached.init(node) do
def handle_event("init", %{"data" => data}, socket) do
node = Utils.node_from_name(data["name"])
cookie = String.to_atom(data["cookie"])
case Runtime.Attached.init(node, cookie) do
{:ok, runtime} ->
Session.connect_runtime(socket.assigns.session_id, runtime)
{:noreply, assign(socket, name: name, error_message: nil)}
{:noreply, assign(socket, data: data, error_message: nil)}
{:error, error} ->
message = runtime_error_to_message(error)
{:noreply, assign(socket, name: name, error_message: message)}
{:noreply, assign(socket, data: data, error_message: message)}
end
end
defp initial_name(%Runtime.Attached{} = current_runtime), do: current_runtime.node
defp initial_name(_runtime), do: ""
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
defp runtime_error_to_message(:unreachable), do: "Node unreachable"

View file

@ -16,7 +16,7 @@ defmodule LivebookWeb.SessionLive.CellSettingsComponent do
@impl true
def render(assigns) do
~L"""
<div class="p-6 pb-4 max-w-xl w-screen flex flex-col space-y-8">
<div class="p-6 pb-4 flex flex-col space-y-8">
<h3 class="text-2xl font-semibold text-gray-800">
Cell settings
</h3>

View file

@ -11,7 +11,7 @@ defmodule LivebookWeb.SessionLive.CellUploadComponent do
@impl true
def render(assigns) do
~L"""
<div class="p-6 pb-4 max-w-xl w-screen flex flex-col space-y-8">
<div class="p-6 pb-4 flex flex-col space-y-8">
<h3 class="text-2xl font-semibold text-gray-800">
Insert image
</h3>

View file

@ -13,7 +13,7 @@ defmodule LivebookWeb.SessionLive.PersistenceComponent do
@impl true
def render(assigns) do
~L"""
<div class="p-6 pb-4 max-w-4xl w-screen flex flex-col space-y-3">
<div class="p-6 pb-4 flex flex-col space-y-3">
<h3 class="text-2xl font-semibold text-gray-800">
File
</h3>

View file

@ -30,7 +30,7 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do
@impl true
def render(assigns) do
~L"""
<div class="p-6 pb-4 max-w-4xl w-screen flex flex-col space-y-3">
<div class="p-6 pb-4 max-w-4xl flex flex-col space-y-3">
<h3 class="text-2xl font-semibold text-gray-800">
Runtime
</h3>

View file

@ -42,7 +42,7 @@ defmodule LivebookWeb.SessionLive.ShortcutsComponent do
@impl true
def render(assigns) do
~L"""
<div class="p-6 sm:max-w-5xl sm:w-full flex flex-col space-y-3">
<div class="p-6 flex flex-col space-y-3">
<h3 class="text-2xl font-semibold text-gray-800">
Keyboard shortcuts
</h3>